emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/pgtk f6d8c59 001/100: Introduce Pure GTK3 port


From: Yuuki Harano
Subject: feature/pgtk f6d8c59 001/100: Introduce Pure GTK3 port
Date: Tue, 24 Nov 2020 08:02:23 -0500 (EST)

branch: feature/pgtk
commit f6d8c5939bff5b5baf46578718999e06061e26ff
Author: Yuuki Harano <masm@masm11.ddo.jp>
Commit: Jeff Walsh <fejfighter@gmail.com>

    Introduce Pure GTK3 port
    
    * src/xsettings.h:
    
    * src/xsettings.c:
    (dpyinfo_valid, store_tool_bar_style_changed)
    (XSETTINGS_FONT_NAME, get_prop_window, read_settings)
    (apply_xft_settings, read_and_apply_settings)
    (xft_settings_event, init_xsettings, xsettings_initialize):
    
    * src/xfaces.c:
    (x_create_gc, x_free_gc):
    
    * src/xdisp.c (redisplay_tool_bar, redisplay_internal)
    (draw_glyphs_debug, draw_glyphs, mouse_face_from_buffer_pos)
    (note_mouse_highlight):
    
    * src/terminal.c (Fterminal_live_p):
    
    * src/termhooks.h (enum output_method, GCALIGNED_STRUCT)
    (TERMINAL_FONT_CACHE):
    
    * src/process.c (wait_reading_process_output):
    
    * src/pgtkterm.h:
    
    * src/pgtkterm.c:
    
    * src/pgtkselect.h:
    
    * src/pgtkselect.c:
    
    * src/pgtkgui.h:
    
    * src/pgtkfns.c:
    
    * src/image.c:
    (XGetPixel, XPutPixel, image_create_bitmap_from_data)
    (image_create_bitmap_from_file, free_bitmap_record)
    (image_destroy_x_image, gui_put_x_image, image_get_x_image)
    (Create_Pixmap_From_Bitmap_Data, xbm_load_image, )
    (xpm_load_image, lookup_rgb_color, image_disable_image)
    (image_build_heuristic_mask, imagemagick_load_image):
    
    * src/gtkutil.h:
    
    * src/gtkutil.c (PGTK_TRACE, xg_set_screen, xg_display_open)
    (xg_display_close, xg_create_default_cursor)
    (xg_get_pixbuf_from_pix_and_mask, xg_check_special_colors)
    (qttip_cb, hierarchy_ch_cb, xg_prepare_tooltip, )
    (xg_show_tooltip, xg_hide_tooltip, xg_frame_resized)
    (xg_frame_set_char_size, xg_height_or_width_changed)
    (xg_set_widget_bg, style_changed_cb, xg_create_frame_widgets)
    (xg_free_frame_widgets, x_wm_set_size_hint, xg_frame_restack)
    (xg_mark_data, xg_update_frame_menubar, free_frame_menubar)
    (xg_update_submenu, xg_finish_scroll_bar_creation)
    (xg_update_scrollbar_pos, xg_update_horizontal_scrollbar_pos)
    (xg_set_toolkit_scroll_bar_thumb, xg_event_is_for_scrollbar)
    (draw_page, xg_pack_tool_bar, xg_create_tool_bar)
    (xg_update_tool_bar_sizes, update_frame_tool_bar)
    (free_frame_tool_bar, xg_change_toolbar_position):
    
    * src/ftcrfont.c:
    (ftcrfont_draw):
    
    * src/fringe.c:
    (init_fringe_bitmap):
    
    * src/frame.h (GCALIGNED_STRUCT, FRAME_WINDOW_P):
    
    * src/frame.c (Fframep):
    
    * src/font.h:
    
    * src/font.c (syms_of_font):
    
    * src/emacsgtkfixed.c:
    (emacs_fixed_get_preferred_width)
    (emacs_fixed_get_preferred_height, XSetWMSizeHints):
    
    * src/emacs.c (main):
    
    * src/dispnew.c (init_display_interactive):
    
    * src/dispextern.h:
    
    * src/alloc.c:
    (garbage_collect):
    
    * src/Makefile.in (PGTK_OBJ, PGTK_LIBS, base_obj, LIBES):
    
    * src/.gdbinit:
    
    * lisp/url/url-privacy.el (url-setup-privacy-info):
    
    * lisp/term/pgtk-win.el (featurep):
    
    * lisp/startup.el (command-line, fancy-splash-frame):
    
    * lisp/net/eww.el (eww-form-submit, eww-form-file)
    (eww-form-checkbox, eww-form-select):
    
    * lisp/mwheel.el (mouse-wheel-down-event, mouse-wheel-up-event):
    
    * lisp/loadup.el (featurep):
    
    * lisp/international/mule-cmds.el (set-coding-system-map):
    
    * lisp/frame.el (pgtk-frame-geometry, frame-geometry)
    (w32-frame-edges, frame-edges)
    (pgtk-mouse-absolute-pixel-position)
    (mouse-absolute-pixel-position)
    (pgtk-set-mouse-absolute-pixel-position)
    (pgtk-frame-list-z-order, frame-list-z-order)
    (pgtk-frame-restack, frame-restack, display-mouse-p)
    (display-graphic-p, display-symbol-keys-p, )
    (display-pixel-height, display-mm-height, display-mm-width)
    (display-backing-store, display-save-under, display-color-cells)
    (display-planes, display-visual-class)
    (pgtk-display-monitor-attributes-list)
    (display-monitor-attributes-list):
    
    * lisp/faces.el (face-spec-set-match-display, tool-bar):
    
    * lisp/cus-edit.el (custom-button, custom-button-mouse)
    (custom-button-pressed, custom-display):
    
    * configure.ac (AUTO_DEPEND, XARGS_LIMIT, XWIDGETS_OBJ):
---
 configure.ac                      |   91 +-
 lisp/cus-edit.el                  |   10 +-
 lisp/faces.el                     |    4 +-
 lisp/frame.el                     |   54 +-
 lisp/international/mule-cmds.el   |    2 +-
 lisp/loadup.el                    |    7 +
 lisp/mwheel.el                    |    8 +-
 lisp/net/eww.el                   |    8 +-
 lisp/startup.el                   |    4 +-
 lisp/term/pgtk-win.el             |  429 +++
 lisp/url/url-privacy.el           |    1 +
 src/.gdbinit                      |    4 +
 src/Makefile.in                   |    7 +-
 src/alloc.c                       |    7 +-
 src/dispextern.h                  |   11 +
 src/dispnew.c                     |   13 +
 src/emacs.c                       |   15 +-
 src/emacsgtkfixed.c               |   21 +
 src/font.c                        |    6 +-
 src/font.h                        |    2 +-
 src/frame.c                       |    8 +-
 src/frame.h                       |   11 +-
 src/fringe.c                      |   23 +-
 src/ftcrfont.c                    |   20 +
 src/ftfont.h                      |    1 +
 src/gtkutil.c                     |  327 +-
 src/gtkutil.h                     |   16 +-
 src/image.c                       |   53 +-
 src/lisp.h                        |    2 +-
 src/pgtkfns.c                     | 2778 ++++++++++++++++
 src/pgtkgui.h                     |  136 +
 src/pgtkselect.c                  |  466 +++
 src/{xsettings.h => pgtkselect.h} |   24 +-
 src/pgtkterm.c                    | 6395 +++++++++++++++++++++++++++++++++++++
 src/pgtkterm.h                    |  569 ++++
 src/process.c                     |    6 +-
 src/termhooks.h                   |    9 +-
 src/terminal.c                    |    2 +
 src/xdisp.c                       |   18 +-
 src/xfaces.c                      |   24 +
 src/xsettings.c                   |   54 +-
 src/xsettings.h                   |   15 +-
 42 files changed, 11514 insertions(+), 147 deletions(-)

diff --git a/configure.ac b/configure.ac
index 888b415..b168131 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1833,6 +1833,8 @@ window_system=none
 AC_PATH_X
 if test "$no_x" != yes; then
   window_system=x11
+else
+  window_system=pgtk
 fi
 
 LD_SWITCH_X_SITE_RPATH=
@@ -2239,6 +2241,11 @@ dnl use the toolkit if we have gtk, or X11R5 or newer.
   w32 )
     term_header=w32term.h
   ;;
+  pgtk )
+    term_header=pgtkterm.h
+    with_gtk3=yes
+    USE_X_TOOLKIT=none
+  ;;
 esac
 
 if test "$window_system" = none && test "X$with_x" != "Xno"; then
@@ -2567,7 +2574,7 @@ fi
 
 ### Use -lrsvg-2 if available, unless '--with-rsvg=no' is specified.
 HAVE_RSVG=no
-if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" || test "${opsys}" 
= "mingw32"; then
+if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" || test 
"${window_system}" = "pgtk" || test "${opsys}" = "mingw32"; then
   if test "${with_rsvg}" != "no"; then
     RSVG_REQUIRED=2.14.0
     RSVG_MODULE="librsvg-2.0 >= $RSVG_REQUIRED"
@@ -2588,7 +2595,7 @@ if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = 
"yes" || test "${opsys}" =
 fi
 
 HAVE_IMAGEMAGICK=no
-if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" || test 
"${HAVE_W32}" = "yes"; then
+if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" || test 
"${window_system}" = "pgtk" || test "${HAVE_W32}" = "yes"; then
   if test "${with_imagemagick}" != "no"; then
     if test -n "$BREW"; then
       # Homebrew doesn't link ImageMagick 6 by default, so make sure
@@ -2605,6 +2612,11 @@ if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = 
"yes" || test "${HAVE_W32}"
        EMACS_CHECK_MODULES([IMAGEMAGICK], [Wand >= 6.3.5 Wand != 6.8.2])
     fi
 
+    if test $HAVE_IMAGEMAGICK != yes; then
+      IMAGEMAGICK_MODULE="MagickWand-6.Q16HDRI >= 6.3.5 MagickWand-6.Q16HDRI 
!= 6.8.2 MagickWand-6.Q16HDRI < 7 MagickCore-6.Q16HDRI >= 6.9.9 
MagickCore-6.Q16HDRI < 7"
+      EMACS_CHECK_MODULES([IMAGEMAGICK], [$IMAGEMAGICK_MODULE])
+    fi
+
     if test $HAVE_IMAGEMAGICK = yes; then
       OLD_CFLAGS=$CFLAGS
       OLD_LIBS=$LIBS
@@ -2812,6 +2824,16 @@ AC_SUBST(XWIDGETS_OBJ)
 CFLAGS=$OLD_CFLAGS
 LIBS=$OLD_LIBS
 
+PGTK_OBJ=
+PGTK_LIBS=
+if test "$window_system" = "pgtk"; then
+  PGTK_OBJ="pgtkfns.o pgtkterm.o pgtkselect.o xsettings.o"
+  PGTK_LIBS="$GTK_LIBS -ldl"
+  AC_DEFINE([HAVE_PGTK], 1, [Define to 1 if you have pure Gtk+-3.])
+fi
+AC_SUBST(PGTK_OBJ)
+AC_SUBST(PGTK_LIBS)
+
 dnl D-Bus has been tested under GNU/Linux only.  Must be adapted for
 dnl other platforms.
 HAVE_DBUS=no
@@ -2841,7 +2863,7 @@ AC_SUBST(DBUS_OBJ)
 
 dnl GSettings has been tested under GNU/Linux only.
 HAVE_GSETTINGS=no
-if test "${HAVE_X11}" = "yes" && test "${with_gsettings}" = "yes"; then
+if test "${HAVE_X11}" = "yes" -o "${window_system}" = "pgtk" && test 
"${with_gsettings}" = "yes"; then
    EMACS_CHECK_MODULES([GSETTINGS], [gio-2.0 >= 2.26])
    if test "$HAVE_GSETTINGS" = "yes"; then
       old_CFLAGS=$CFLAGS
@@ -2875,7 +2897,7 @@ fi
 dnl GConf has been tested under GNU/Linux only.
 dnl The version is really arbitrary, it is about the same age as Gtk+ 2.6.
 HAVE_GCONF=no
-if test "${HAVE_X11}" = "yes" && test "${with_gconf}" != "no"; then
+if test "${HAVE_X11}" = "yes" -o "${window_system}" = "pgtk" && test 
"${with_gconf}" != "no"; then
    EMACS_CHECK_MODULES([GCONF], [gconf-2.0 >= 2.13])
    if test "$HAVE_GCONF" = yes; then
       AC_DEFINE(HAVE_GCONF, 1, [Define to 1 if using GConf.])
@@ -3437,10 +3459,34 @@ if test "${HAVE_X11}" = "yes"; then
     fi
   fi
 else # "${HAVE_X11}" != "yes"
-  HAVE_XFT=no
-  HAVE_FREETYPE=no
-  HAVE_LIBOTF=no
-  HAVE_M17N_FLT=no
+  if test $window_system = pgtk; then
+    EMACS_CHECK_MODULES([FONTCONFIG], [fontconfig >= 2.2.0])
+    EMACS_CHECK_MODULES([FREETYPE], [freetype2])
+    if test "$HAVE_FONTCONFIG" != yes -o "$HAVE_FREETYPE" != yes; then
+      AC_MSG_ERROR(fontconfig and freetype is required.)
+    fi
+    HAVE_LIBOTF=no
+    AC_DEFINE(HAVE_FREETYPE, 1,
+             [Define to 1 if using the freetype and fontconfig libraries.])
+    if test "${with_libotf}" != "no"; then
+      EMACS_CHECK_MODULES([LIBOTF], [libotf])
+      if test "$HAVE_LIBOTF" = "yes"; then
+       AC_DEFINE(HAVE_LIBOTF, 1, [Define to 1 if using libotf.])
+       AC_CHECK_LIB(otf, OTF_get_variation_glyphs,
+                    HAVE_OTF_GET_VARIATION_GLYPHS=yes,
+                    HAVE_OTF_GET_VARIATION_GLYPHS=no)
+       if test "${HAVE_OTF_GET_VARIATION_GLYPHS}" = "yes"; then
+         AC_DEFINE(HAVE_OTF_GET_VARIATION_GLYPHS, 1,
+                   [Define to 1 if libotf has OTF_get_variation_glyphs.])
+       fi
+      fi
+    fi
+  else
+    HAVE_XFT=no
+    HAVE_FREETYPE=no
+    HAVE_LIBOTF=no
+    HAVE_M17N_FLT=no
+  fi
 fi   # "${HAVE_X11}" != "yes"
 
 HAVE_HARFBUZZ=no
@@ -3452,6 +3498,7 @@ else
   harfbuzz_required_ver=0.9.42
 fi
 if test "${HAVE_X11}" = "yes" && test "${HAVE_FREETYPE}" = "yes" \
+        || test "$window_system" = "pgtk" \
         || test "${HAVE_W32}" = "yes"; then
   if test "${with_harfbuzz}" != "no"; then
     EMACS_CHECK_MODULES([HARFBUZZ], [harfbuzz >= $harfbuzz_required_ver])
@@ -3479,6 +3526,25 @@ AC_SUBST(LIBOTF_LIBS)
 AC_SUBST(M17N_FLT_CFLAGS)
 AC_SUBST(M17N_FLT_LIBS)
 
+HAVE_CAIRO=no
+if test "${HAVE_X11}" = "yes" -o "$window_system" = pgtk; then
+  if test "${with_cairo}" != "no"; then
+    CAIRO_REQUIRED=1.12.0
+    CAIRO_MODULE="cairo >= $CAIRO_REQUIRED"
+    EMACS_CHECK_MODULES(CAIRO, $CAIRO_MODULE)
+    if test $HAVE_CAIRO = yes; then
+      AC_DEFINE(USE_CAIRO, 1, [Define to 1 if using cairo.])
+    else
+      AC_MSG_ERROR([cairo requested but not found.])
+    fi
+
+    CFLAGS="$CFLAGS $CAIRO_CFLAGS"
+    LIBS="$LIBS $CAIRO_LIBS"
+    AC_SUBST(CAIRO_CFLAGS)
+    AC_SUBST(CAIRO_LIBS)
+  fi
+fi
+
 if test "${HAVE_X11}" = "yes"; then
   AC_CHECK_HEADER(X11/Xlib-xcb.h,
     AC_CHECK_LIB(xcb, xcb_translate_coordinates, HAVE_XCB=yes))
@@ -3740,7 +3806,7 @@ if test "${with_png}" != no; then
   # mingw32 loads the library dynamically.
   if test "$opsys" = mingw32; then
     AC_CHECK_HEADER([png.h], [HAVE_PNG=yes])
-  elif test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
+  elif test "${HAVE_X11}" = "yes" || test "$window_system" = "pgtk" || test 
"${HAVE_W32}" = "yes" \
        || test "${HAVE_NS}" = "yes"; then
     EMACS_CHECK_MODULES([PNG], [libpng >= 1.0.0])
     if test $HAVE_PNG = yes; then
@@ -3815,7 +3881,7 @@ if test "${opsys}" = "mingw32"; then
   if test "${HAVE_TIFF}" = "yes"; then
     AC_DEFINE(HAVE_TIFF, 1, [Define to 1 if you have the tiff library 
(-ltiff).])
   fi
-elif test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
+elif test "${HAVE_X11}" = "yes" || test "${window_system}" = "pgtk" || test 
"${HAVE_W32}" = "yes" \
      || test "${HAVE_NS}" = "yes"; then
   if test "${with_tiff}" != "no"; then
     AC_CHECK_HEADER(tiffio.h,
@@ -3844,7 +3910,7 @@ if test "${opsys}" = "mingw32"; then
   if test "${HAVE_GIF}" = "yes"; then
     AC_DEFINE(HAVE_GIF, 1, [Define to 1 if you have a gif (or ungif) library.])
   fi
-elif test "${HAVE_X11}" = "yes" && test "${with_gif}" != "no" \
+elif test "${HAVE_X11}" = "yes" -o "${window_system}" = "pgtk"  && test 
"${with_gif}" != "no" \
         || test "${HAVE_W32}" = "yes" || test "${HAVE_NS}" = "yes"; then
   AC_CHECK_HEADER(gif_lib.h,
 # EGifPutExtensionLast only exists from version libungif-4.1.0b1.
@@ -5290,6 +5356,9 @@ if test "${HAVE_X_WINDOWS}" = "yes" ; then
     FONT_OBJ="$FONT_OBJ ftfont.o"
   fi
 fi
+if test "${window_system}" = "pgtk"; then
+   FONT_OBJ="ftfont.o ftcrfont.o"
+fi
 if test "${HAVE_HARFBUZZ}" = "yes" ; then
   FONT_OBJ="$FONT_OBJ hbfont.o"
 fi
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index eceba8f..099b7da 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -2172,7 +2172,7 @@ and `face'."
 ;;; The `custom' Widget.
 
 (defface custom-button
-  '((((type x w32 ns) (class color))   ; Like default mode line
+  '((((type x w32 ns pgtk) (class color))      ; Like default mode line
      :box (:line-width 2 :style released-button)
      :background "lightgrey" :foreground "black"))
   "Face for custom buffer buttons if `custom-raised-buttons' is non-nil."
@@ -2180,7 +2180,7 @@ and `face'."
   :group 'custom-faces)
 
 (defface custom-button-mouse
-  '((((type x w32 ns) (class color))
+  '((((type x w32 ns pgtk) (class color))
      :box (:line-width 2 :style released-button)
      :background "grey90" :foreground "black")
     (t
@@ -2205,7 +2205,7 @@ and `face'."
       (if custom-raised-buttons 'custom-button-mouse 'highlight))
 
 (defface custom-button-pressed
-  '((((type x w32 ns) (class color))
+  '((((type x w32 ns pgtk) (class color))
      :box (:line-width 2 :style pressed-button)
      :background "lightgrey" :foreground "black")
     (t :inverse-video t))
@@ -3445,6 +3445,10 @@ MS Windows.")
                                           :sibling-args (:help-echo "\
 GNUstep or Macintosh OS Cocoa interface.")
                                           ns)
+                                   (const :format "PGTK "
+                                          :sibling-args (:help-echo "\
+Pure-GTK interface.")
+                                          ns)
                                    (const :format "DOS "
                                           :sibling-args (:help-echo "\
 Plain MS-DOS.")
diff --git a/lisp/faces.el b/lisp/faces.el
index 7355e1d..5e525e7 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -1487,7 +1487,7 @@ If FRAME is nil, the current FRAME is used."
            match (cond ((eq req 'type)
                         (or (memq (window-system frame) options)
                             (and (memq 'graphic options)
-                                 (memq (window-system frame) '(x w32 ns)))
+                                 (memq (window-system frame) '(x w32 ns pgtk)))
                             ;; FIXME: This should be revisited to use
                             ;; display-graphic-p, provided that the
                             ;; color selection depends on the number
@@ -2755,7 +2755,7 @@ Note: Other faces cannot inherit from the cursor face."
   '((default
      :box (:line-width 1 :style released-button)
      :foreground "black")
-    (((type x w32 ns) (class color))
+    (((type x w32 ns pgtk) (class color))
      :background "grey75")
     (((type x) (class mono))
      :background "grey"))
diff --git a/lisp/frame.el b/lisp/frame.el
index 772ba3d..a43e12f 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -1601,6 +1601,7 @@ live frame and defaults to the selected one."
 (declare-function x-frame-geometry "xfns.c" (&optional frame))
 (declare-function w32-frame-geometry "w32fns.c" (&optional frame))
 (declare-function ns-frame-geometry "nsfns.m" (&optional frame))
+(declare-function pgtk-frame-geometry "pgtkfns.c" (&optional frame))
 
 (defun frame-geometry (&optional frame)
   "Return geometric attributes of FRAME.
@@ -1650,6 +1651,8 @@ and width values are in pixels.
       (w32-frame-geometry frame))
      ((eq frame-type 'ns)
       (ns-frame-geometry frame))
+     ((eq frame-type 'pgtk)
+      (pgtk-frame-geometry frame))
      (t
       (list
        '(outer-position 0 . 0)
@@ -1696,6 +1699,7 @@ selected frame."
 (declare-function x-frame-edges "xfns.c" (&optional frame type))
 (declare-function w32-frame-edges "w32fns.c" (&optional frame type))
 (declare-function ns-frame-edges "nsfns.m" (&optional frame type))
+(declare-function pgtk-frame-edges "pgtkfns.c" (&optional frame type))
 
 (defun frame-edges (&optional frame type)
   "Return coordinates of FRAME's edges.
@@ -1719,12 +1723,15 @@ FRAME."
       (w32-frame-edges frame type))
      ((eq frame-type 'ns)
       (ns-frame-edges frame type))
+     ((eq frame-type 'pgtk)
+      (pgtk-frame-edges frame type))
      (t
       (list 0 0 (frame-width frame) (frame-height frame))))))
 
 (declare-function w32-mouse-absolute-pixel-position "w32fns.c")
 (declare-function x-mouse-absolute-pixel-position "xfns.c")
 (declare-function ns-mouse-absolute-pixel-position "nsfns.m")
+(declare-function pgtk-mouse-absolute-pixel-position "pgtkfns.c")
 
 (defun mouse-absolute-pixel-position ()
   "Return absolute position of mouse cursor in pixels.
@@ -1739,9 +1746,12 @@ position (0, 0) of the selected frame's terminal."
       (w32-mouse-absolute-pixel-position))
      ((eq frame-type 'ns)
       (ns-mouse-absolute-pixel-position))
+     ((eq frame-type 'pgtk)
+      (pgtk-mouse-absolute-pixel-position))
      (t
       (cons 0 0)))))
 
+(declare-function pgtk-set-mouse-absolute-pixel-position "pgtkfns.c" (x y))
 (declare-function ns-set-mouse-absolute-pixel-position "nsfns.m" (x y))
 (declare-function w32-set-mouse-absolute-pixel-position "w32fns.c" (x y))
 (declare-function x-set-mouse-absolute-pixel-position "xfns.c" (x y))
@@ -1752,6 +1762,8 @@ The coordinates X and Y are interpreted in pixels 
relative to a
 position (0, 0) of the selected frame's terminal."
   (let ((frame-type (framep-on-display)))
     (cond
+     ((eq frame-type 'pgtk)
+      (pgtk-set-mouse-absolute-pixel-position x y))
      ((eq frame-type 'ns)
       (ns-set-mouse-absolute-pixel-position x y))
      ((eq frame-type 'x)
@@ -1850,6 +1862,7 @@ workarea attribute."
 (declare-function x-frame-list-z-order "xfns.c" (&optional display))
 (declare-function w32-frame-list-z-order "w32fns.c" (&optional display))
 (declare-function ns-frame-list-z-order "nsfns.m" (&optional display))
+(declare-function pgtk-frame-list-z-order "pgtkfns.c" (&optional display))
 
 (defun frame-list-z-order (&optional display)
   "Return list of Emacs' frames, in Z (stacking) order.
@@ -1869,11 +1882,14 @@ Return nil if DISPLAY contains no Emacs frame."
      ((eq frame-type 'w32)
       (w32-frame-list-z-order display))
      ((eq frame-type 'ns)
-      (ns-frame-list-z-order display)))))
+      (ns-frame-list-z-order display))
+     ((eq frame-type 'pgtk)
+      (pgtk-frame-list-z-order display)))))
 
 (declare-function x-frame-restack "xfns.c" (frame1 frame2 &optional above))
 (declare-function w32-frame-restack "w32fns.c" (frame1 frame2 &optional above))
 (declare-function ns-frame-restack "nsfns.m" (frame1 frame2 &optional above))
+(declare-function pgtk-frame-restack "pgtkfns.c" (frame1 frame2 &optional 
above))
 
 (defun frame-restack (frame1 frame2 &optional above)
   "Restack FRAME1 below FRAME2.
@@ -1903,7 +1919,9 @@ Some window managers may refuse to restack windows."
          ((eq frame-type 'w32)
           (w32-frame-restack frame1 frame2 above))
          ((eq frame-type 'ns)
-          (ns-frame-restack frame1 frame2 above))))
+          (ns-frame-restack frame1 frame2 above))
+         ((eq frame-type 'pgtk)
+          (pgtk-frame-restack frame1 frame2 above))))
     (error "Cannot restack frames")))
 
 (defun frame-size-changed-p (&optional frame)
@@ -1950,7 +1968,7 @@ frame's display)."
      ((eq frame-type 'w32)
       (with-no-warnings
        (> w32-num-mouse-buttons 0)))
-     ((memq frame-type '(x ns))
+     ((memq frame-type '(x ns pgtk))
       t)    ;; We assume X and NeXTstep *always* have a pointing device
      (t
       (or (and (featurep 'xt-mouse)
@@ -1976,7 +1994,7 @@ frames and several different fonts at once.  This is true 
for displays
 that use a window system such as X, and false for text-only terminals.
 DISPLAY can be a display name, a frame, or nil (meaning the selected
 frame's display)."
-  (not (null (memq (framep-on-display display) '(x w32 ns)))))
+  (not (null (memq (framep-on-display display) '(x w32 ns pgtk)))))
 
 (defun display-images-p (&optional display)
   "Return non-nil if DISPLAY can display images.
@@ -2004,7 +2022,7 @@ frame's display)."
       ;; a Windows DOS Box.
       (with-no-warnings
        (not (null dos-windows-version))))
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns pgtk))
       t)
      (t
       nil))))
@@ -2014,7 +2032,7 @@ frame's display)."
 This means that, for example, DISPLAY can differentiate between
 the keybinding RET and [return]."
   (let ((frame-type (framep-on-display display)))
-    (or (memq frame-type '(x w32 ns pc))
+    (or (memq frame-type '(x w32 ns pc pgtk))
         ;; MS-DOS and MS-Windows terminals have built-in support for
         ;; function (symbol) keys
         (memq system-type '(ms-dos windows-nt)))))
@@ -2027,7 +2045,7 @@ DISPLAY should be either a frame or a display name (a 
string).
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns pgtk))
       (x-display-screens display))
      (t
       1))))
@@ -2047,7 +2065,7 @@ with DISPLAY.  To get information for each physical 
monitor, use
 `display-monitor-attributes-list'."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns pgtk))
       (x-display-pixel-height display))
      (t
       (frame-height (if (framep display) display (selected-frame)))))))
@@ -2067,7 +2085,7 @@ with DISPLAY.  To get information for each physical 
monitor, use
 `display-monitor-attributes-list'."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns pgtk))
       (x-display-pixel-width display))
      (t
       (frame-width (if (framep display) display (selected-frame)))))))
@@ -2105,7 +2123,7 @@ For graphical terminals, note that on \"multi-monitor\" 
setups this
 refers to the height in millimeters for all physical monitors
 associated with DISPLAY.  To get information for each physical
 monitor, use `display-monitor-attributes-list'."
-  (and (memq (framep-on-display display) '(x w32 ns))
+  (and (memq (framep-on-display display) '(x w32 ns pgtk))
        (or (cddr (assoc (or display (frame-parameter nil 'display))
                        display-mm-dimensions-alist))
           (cddr (assoc t display-mm-dimensions-alist))
@@ -2126,7 +2144,7 @@ For graphical terminals, note that on \"multi-monitor\" 
setups this
 refers to the width in millimeters for all physical monitors
 associated with DISPLAY.  To get information for each physical
 monitor, use `display-monitor-attributes-list'."
-  (and (memq (framep-on-display display) '(x w32 ns))
+  (and (memq (framep-on-display display) '(x w32 ns pgtk))
        (or (cadr (assoc (or display (frame-parameter nil 'display))
                        display-mm-dimensions-alist))
           (cadr (assoc t display-mm-dimensions-alist))
@@ -2144,7 +2162,7 @@ DISPLAY can be a display name or a frame.
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns pgtk))
       (x-display-backing-store display))
      (t
       'not-useful))))
@@ -2157,7 +2175,7 @@ DISPLAY can be a display name or a frame.
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns pgtk))
       (x-display-save-under display))
      (t
       'not-useful))))
@@ -2170,7 +2188,7 @@ DISPLAY can be a display name or a frame.
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns pgtk))
       (x-display-planes display))
      ((eq frame-type 'pc)
       4)
@@ -2185,7 +2203,7 @@ DISPLAY can be a display name or a frame.
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns pgtk))
       (x-display-color-cells display))
      ((eq frame-type 'pc)
       16)
@@ -2202,7 +2220,7 @@ DISPLAY can be a display name or a frame.
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns pgtk))
       (x-display-visual-class display))
      ((and (memq frame-type '(pc t))
           (tty-display-color-p display))
@@ -2216,6 +2234,8 @@ If DISPLAY is omitted or nil, it defaults to the selected 
frame's display."
                  (&optional display))
 (declare-function ns-display-monitor-attributes-list "nsfns.m"
                  (&optional terminal))
+(declare-function pgtk-display-monitor-attributes-list "pgtkfns.c"
+                 (&optional terminal))
 
 (defun display-monitor-attributes-list (&optional display)
   "Return a list of physical monitor attributes on DISPLAY.
@@ -2264,6 +2284,8 @@ monitors."
       (w32-display-monitor-attributes-list display))
      ((eq frame-type 'ns)
       (ns-display-monitor-attributes-list display))
+     ((eq frame-type 'pgtk)
+      (pgtk-display-monitor-attributes-list display))
      (t
       (let ((geometry (list 0 0 (display-pixel-width display)
                            (display-pixel-height display))))
diff --git a/lisp/international/mule-cmds.el b/lisp/international/mule-cmds.el
index d361971..e22876b 100644
--- a/lisp/international/mule-cmds.el
+++ b/lisp/international/mule-cmds.el
@@ -88,7 +88,7 @@
     (bindings--define-key map [separator-3] menu-bar-separator)
     (bindings--define-key map [set-terminal-coding-system]
       '(menu-item "For Terminal" set-terminal-coding-system
-        :enable (null (memq initial-window-system '(x w32 ns)))
+        :enable (null (memq initial-window-system '(x w32 ns pgtk)))
         :help "How to encode terminal output"))
     (bindings--define-key map [set-keyboard-coding-system]
       '(menu-item "For Keyboard" set-keyboard-coding-system
diff --git a/lisp/loadup.el b/lisp/loadup.el
index 4b711ee..50ed557 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -336,6 +336,13 @@
         (load "international/mule-util")
         (load "international/ucs-normalize")
         (load "term/ns-win"))))
+(if (featurep 'pgtk)
+    (progn
+      (load "term/common-win")
+      ;; Don't load ucs-normalize.el unless uni-*.el files were
+      ;; already produced, because it needs uni-*.el files that might
+      ;; not be built early enough during bootstrap.
+      (load "term/pgtk-win")))
 (if (fboundp 'x-create-frame)
     ;; Do it after loading term/foo-win.el since the value of the
     ;; mouse-wheel-*-event vars depends on those files being loaded or not.
diff --git a/lisp/mwheel.el b/lisp/mwheel.el
index 1d9fe68..9dcdbe2 100644
--- a/lisp/mwheel.el
+++ b/lisp/mwheel.el
@@ -52,7 +52,7 @@
   (when (bound-and-true-p mouse-wheel-mode) (mouse-wheel-mode 1)))
 
 (defcustom mouse-wheel-down-event
-  (if (or (featurep 'w32-win) (featurep 'ns-win))
+  (if (or (featurep 'w32-win) (featurep 'ns-win) (featurep 'pgtk))
       'wheel-up
     'mouse-4)
   "Event used for scrolling down."
@@ -61,7 +61,7 @@
   :set 'mouse-wheel-change-button)
 
 (defcustom mouse-wheel-up-event
-  (if (or (featurep 'w32-win) (featurep 'ns-win))
+  (if (or (featurep 'w32-win) (featurep 'ns-win) (featurep 'pgtk))
       'wheel-down
     'mouse-5)
   "Event used for scrolling up."
@@ -215,13 +215,13 @@ Also see `mouse-wheel-tilt-scroll'."
   "Function that does the job of scrolling right.")
 
 (defvar mouse-wheel-left-event
-  (if (or (featurep 'w32-win) (featurep 'ns-win))
+  (if (or (featurep 'w32-win) (featurep 'ns-win) (featurep 'pgtk))
       'wheel-left
     'mouse-6)
   "Event used for scrolling left.")
 
 (defvar mouse-wheel-right-event
-  (if (or (featurep 'w32-win) (featurep 'ns-win))
+  (if (or (featurep 'w32-win) (featurep 'ns-win) (featurep 'pgtk))
       'wheel-right
     'mouse-7)
   "Event used for scrolling right.")
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index 9ed01ec..743abb0 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -189,7 +189,7 @@ See also `eww-form-checkbox-selected-symbol'."
                  string))
 
 (defface eww-form-submit
-  '((((type x w32 ns) (class color))   ; Like default mode line
+  '((((type x w32 ns pgtk) (class color))      ; Like default mode line
      :box (:line-width 2 :style released-button)
      :background "#808080" :foreground "black"))
   "Face for eww buffer buttons."
@@ -197,7 +197,7 @@ See also `eww-form-checkbox-selected-symbol'."
   :group 'eww)
 
 (defface eww-form-file
-  '((((type x w32 ns) (class color))   ; Like default mode line
+  '((((type x w32 ns pgtk) (class color))      ; Like default mode line
      :box (:line-width 2 :style released-button)
      :background "#808080" :foreground "black"))
   "Face for eww buffer buttons."
@@ -205,7 +205,7 @@ See also `eww-form-checkbox-selected-symbol'."
   :group 'eww)
 
 (defface eww-form-checkbox
-  '((((type x w32 ns) (class color))   ; Like default mode line
+  '((((type x w32 ns pgtk) (class color))      ; Like default mode line
      :box (:line-width 2 :style released-button)
      :background "lightgrey" :foreground "black"))
   "Face for eww buffer buttons."
@@ -213,7 +213,7 @@ See also `eww-form-checkbox-selected-symbol'."
   :group 'eww)
 
 (defface eww-form-select
-  '((((type x w32 ns) (class color))   ; Like default mode line
+  '((((type x w32 ns pgtk) (class color))      ; Like default mode line
      :box (:line-width 2 :style released-button)
      :background "lightgrey" :foreground "black"))
   "Face for eww buffer buttons."
diff --git a/lisp/startup.el b/lisp/startup.el
index 9f67dfd..e3c792e 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -1312,7 +1312,7 @@ please check its value")
   ;; only because all other settings of no-blinking-cursor are here.
   (unless (or noninteractive
              emacs-basic-display
-             (and (memq window-system '(x w32 ns))
+             (and (memq window-system '(x w32 ns pgtk))
                   (not (member (x-get-resource "cursorBlink" "CursorBlink")
                                '("no" "off" "false" "0")))))
     (setq no-blinking-cursor t))
@@ -1962,6 +1962,8 @@ we put it on this frame."
     ;; frame visible.
     (if (eq (window-system) 'w32)
        (sit-for 0 t))
+    (if (eq (window-system) 'pgtk)
+       (sit-for 0.1 t))
     (dolist (frame (append (frame-list) (list (selected-frame))))
       (if (and (frame-visible-p frame)
               (not (window-minibuffer-p (frame-selected-window frame))))
diff --git a/lisp/term/pgtk-win.el b/lisp/term/pgtk-win.el
new file mode 100644
index 0000000..203cce4
--- /dev/null
+++ b/lisp/term/pgtk-win.el
@@ -0,0 +1,429 @@
+;;;
+
+;;; Code:
+(eval-when-compile (require 'cl-lib))
+(or (featurep 'pgtk)
+    (error "%s: Loading pgtk-win.el but not compiled for pure Gtk+-3."
+           (invocation-name)))
+
+;; Documentation-purposes only: actually loaded in loadup.el.
+(require 'term/common-win)
+(require 'frame)
+(require 'mouse)
+(require 'scroll-bar)
+(require 'faces)
+(require 'menu-bar)
+(require 'fontset)
+(require 'dnd)
+
+(defgroup pgtk nil
+  "Pure-GTK specific features."
+  :group 'environment)
+
+;;;; Command line argument handling.
+
+(defvar x-invocation-args)
+;; Set in term/common-win.el; currently unused by Gtk's x-open-connection.
+(defvar x-command-line-resources)
+
+;; pgtkterm.c.
+(defvar pgtk-input-file)
+
+(defun pgtk-handle-nxopen (_switch &optional temp)
+  (setq unread-command-events (append unread-command-events
+                                      (if temp '(pgtk-open-temp-file)
+                                        '(pgtk-open-file)))
+        pgtk-input-file (append pgtk-input-file (list (pop 
x-invocation-args)))))
+
+(defun pgtk-handle-nxopentemp (switch)
+  (pgtk-handle-nxopen switch t))
+
+(defun pgtk-ignore-1-arg (_switch)
+  (setq x-invocation-args (cdr x-invocation-args)))
+
+;;;; File handling.
+
+(defun x-file-dialog (prompt dir default_filename mustmatch only_dir_p)
+"Read file name, prompting with PROMPT in directory DIR.
+Use a file selection dialog.  Select DEFAULT-FILENAME in the dialog's file
+selection box, if specified.  If MUSTMATCH is non-nil, the returned file
+or directory must exist.
+
+This function is only defined on PGTK, MS Windows, and X Windows with the
+Motif or Gtk toolkits.  With the Motif toolkit, ONLY-DIR-P is ignored.
+Otherwise, if ONLY-DIR-P is non-nil, the user can only select directories."
+  (pgtk-read-file-name prompt dir mustmatch default_filename only_dir_p))
+
+(defun pgtk-open-file-using-panel ()
+  "Pop up open-file panel, and load the result in a buffer."
+  (interactive)
+  ;; Prompt dir defaultName isLoad initial.
+  (setq pgtk-input-file (pgtk-read-file-name "Select File to Load" nil t nil))
+  (if pgtk-input-file
+      (and (setq pgtk-input-file (list pgtk-input-file)) (pgtk-find-file))))
+
+(defun pgtk-write-file-using-panel ()
+  "Pop up save-file panel, and save buffer in resulting name."
+  (interactive)
+  (let (pgtk-output-file)
+    ;; Prompt dir defaultName isLoad initial.
+    (setq pgtk-output-file (pgtk-read-file-name "Save As" nil nil nil))
+    (message pgtk-output-file)
+    (if pgtk-output-file (write-file pgtk-output-file))))
+
+(defcustom pgtk-pop-up-frames 'fresh
+  "Non-nil means open files upon request from the Workspace in a new frame.
+If t, always do so.  Any other non-nil value means open a new frame
+unless the current buffer is a scratch buffer."
+  :type '(choice (const :tag "Never" nil)
+                 (const :tag "Always" t)
+                 (other :tag "Except for scratch buffer" fresh))
+  :version "23.1"
+  :group 'pgtk)
+
+(declare-function pgtk-hide-emacs "pgtkfns.c" (on))
+
+(defun pgtk-find-file ()
+  "Do a `find-file' with the `pgtk-input-file' as argument."
+  (interactive)
+  (let* ((f (file-truename
+            (expand-file-name (pop pgtk-input-file)
+                              command-line-default-directory)))
+         (file (find-file-noselect f))
+         (bufwin1 (get-buffer-window file 'visible))
+         (bufwin2 (get-buffer-window "*scratch*" 'visible)))
+    (cond
+     (bufwin1
+      (select-frame (window-frame bufwin1))
+      (raise-frame (window-frame bufwin1))
+      (select-window bufwin1))
+     ((and (eq pgtk-pop-up-frames 'fresh) bufwin2)
+      (pgtk-hide-emacs 'activate)
+      (select-frame (window-frame bufwin2))
+      (raise-frame (window-frame bufwin2))
+      (select-window bufwin2)
+      (find-file f))
+     (pgtk-pop-up-frames
+      (pgtk-hide-emacs 'activate)
+      (let ((pop-up-frames t)) (pop-to-buffer file nil)))
+     (t
+      (pgtk-hide-emacs 'activate)
+      (find-file f)))))
+
+
+(defun pgtk-drag-n-drop (event &optional new-frame force-text)
+  "Edit the files listed in the drag-n-drop EVENT.
+Switch to a buffer editing the last file dropped."
+  (interactive "e")
+  (let* ((window (posn-window (event-start event)))
+         (arg (car (cdr (cdr event))))
+         (type (car arg))
+         (data (car (cdr arg)))
+         (url-or-string (cond ((eq type 'file)
+                               (concat "file:" data))
+                              (t data))))
+    (set-frame-selected-window nil window)
+    (when new-frame
+      (select-frame (make-frame)))
+    (raise-frame)
+    (setq window (selected-window))
+    (if force-text
+        (dnd-insert-text window 'private data)
+      (dnd-handle-one-url window 'private url-or-string))))
+
+
+(defun pgtk-drag-n-drop-other-frame (event)
+  "Edit the files listed in the drag-n-drop EVENT, in other frames.
+May create new frames, or reuse existing ones.  The frame editing
+the last file dropped is selected."
+  (interactive "e")
+  (pgtk-drag-n-drop event t))
+
+(defun pgtk-drag-n-drop-as-text (event)
+  "Drop the data in EVENT as text."
+  (interactive "e")
+  (pgtk-drag-n-drop event nil t))
+
+(defun pgtk-drag-n-drop-as-text-other-frame (event)
+  "Drop the data in EVENT as text in a new frame."
+  (interactive "e")
+  (pgtk-drag-n-drop event t t))
+
+(global-set-key [drag-n-drop] 'pgtk-drag-n-drop)
+(global-set-key [C-drag-n-drop] 'pgtk-drag-n-drop-other-frame)
+(global-set-key [M-drag-n-drop] 'pgtk-drag-n-drop-as-text)
+(global-set-key [C-M-drag-n-drop] 'pgtk-drag-n-drop-as-text-other-frame)
+
+;;;; Frame-related functions.
+
+;; pgtkterm.c
+(defvar pgtk-alternate-modifier)
+(defvar pgtk-right-alternate-modifier)
+(defvar pgtk-right-command-modifier)
+(defvar pgtk-right-control-modifier)
+
+;; You say tomAYto, I say tomAHto..
+(defvaralias 'pgtk-option-modifier 'pgtk-alternate-modifier)
+(defvaralias 'pgtk-right-option-modifier 'pgtk-right-alternate-modifier)
+
+(defun pgtk-do-hide-emacs ()
+  (interactive)
+  (pgtk-hide-emacs t))
+
+(declare-function pgtk-hide-others "pgtkfns.c" ())
+
+(defun pgtk-do-hide-others ()
+  (interactive)
+  (pgtk-hide-others))
+
+(declare-function pgtk-emacs-info-panel "pgtkfns.c" ())
+
+(defun pgtk-do-emacs-info-panel ()
+  (interactive)
+  (pgtk-emacs-info-panel))
+
+(defun pgtk-next-frame ()
+  "Switch to next visible frame."
+  (interactive)
+  (other-frame 1))
+
+(defun pgtk-prev-frame ()
+  "Switch to previous visible frame."
+  (interactive)
+  (other-frame -1))
+
+;; Frame will be focused anyway, so select it
+;; (if this is not done, mode line is dimmed until first interaction)
+;; FIXME: Sounds like we're working around a bug in the underlying code.
+(add-hook 'after-make-frame-functions 'select-frame)
+
+(defvar tool-bar-mode)
+(declare-function tool-bar-mode "tool-bar" (&optional arg))
+
+;; Based on a function by David Reitter <dreitter@inf.ed.ac.uk> ;
+;; see https://lists.gnu.org/archive/html/emacs-devel/2005-09/msg00681.html .
+(defun pgtk-toggle-toolbar (&optional frame)
+  "Switches the tool bar on and off in frame FRAME.
+ If FRAME is nil, the change applies to the selected frame."
+  (interactive)
+  (modify-frame-parameters
+   frame (list (cons 'tool-bar-lines
+                      (if (> (or (frame-parameter frame 'tool-bar-lines) 0) 0)
+                                  0 1)) ))
+  (if (not tool-bar-mode) (tool-bar-mode t)))
+
+
+;;;; Dialog-related functions.
+
+;; Ask user for confirm before printing.  Due to Kevin Rodgers.
+(defun pgtk-print-buffer ()
+  "Interactive front-end to `print-buffer': asks for user confirmation first."
+  (interactive)
+  (if (and (called-interactively-p 'interactive)
+           (or (listp last-nonmenu-event)
+               (and (char-or-string-p (event-basic-type last-command-event))
+                    (memq 'super (event-modifiers last-command-event)))))
+      (let ((last-nonmenu-event (if (listp last-nonmenu-event)
+                                    last-nonmenu-event
+                                  ;; Fake it:
+                                  `(mouse-1 POSITION 1))))
+        (if (y-or-n-p (format "Print buffer %s? " (buffer-name)))
+            (print-buffer)
+         (error "Canceled")))
+    (print-buffer)))
+
+;;;; Font support.
+
+;; Needed for font listing functions under both backend and normal
+(setq scalable-fonts-allowed t)
+
+;; Set to use font panel instead
+(declare-function pgtk-popup-font-panel "pgtkfns.c" (&optional frame))
+(defalias 'x-select-font 'pgtk-popup-font-panel "Pop up the font panel.
+This function has been overloaded in Nextstep.")
+(defalias 'mouse-set-font 'pgtk-popup-font-panel "Pop up the font panel.
+This function has been overloaded in Nextstep.")
+
+;; pgtkterm.c
+(defvar pgtk-input-font)
+(defvar pgtk-input-fontsize)
+
+(defun pgtk-respond-to-change-font ()
+  "Respond to changeFont: event, expecting `pgtk-input-font' and\n\
+`pgtk-input-fontsize' of new font."
+  (interactive)
+  (modify-frame-parameters (selected-frame)
+                           (list (cons 'fontsize pgtk-input-fontsize)))
+  (modify-frame-parameters (selected-frame)
+                           (list (cons 'font pgtk-input-font)))
+  (set-frame-font pgtk-input-font))
+
+
+;; Default fontset.  This is mainly here to show how a fontset
+;; can be set up manually.  Ordinarily, fontsets are auto-created whenever
+;; a font is chosen by
+(defvar pgtk-standard-fontset-spec
+  ;; Only some code supports this so far, so use uglier XLFD version
+  ;; "-pgtk-*-*-*-*-*-10-*-*-*-*-*-fontset-standard,latin:Courier,han:Kai"
+  (mapconcat 'identity
+             '("-*-Monospace-*-*-*-*-10-*-*-*-*-*-fontset-standard"
+               "latin:-*-Courier-*-*-*-*-10-*-*-*-*-*-iso10646-1"
+               "han:-*-Kai-*-*-*-*-10-*-*-*-*-*-iso10646-1"
+               "cyrillic:-*-Trebuchet$MS-*-*-*-*-10-*-*-*-*-*-iso10646-1")
+             ",")
+  "String of fontset spec of the standard fontset.
+This defines a fontset consisting of the Courier and other fonts.
+See the documentation of `create-fontset-from-fontset-spec' for the format.")
+
+
+;;;; Pasteboard support.
+
+(define-obsolete-function-alias 'pgtk-store-cut-buffer-internal
+  'gui-set-selection "24.1")
+
+
+(defun pgtk-copy-including-secondary ()
+  (interactive)
+  (call-interactively 'kill-ring-save)
+  (gui-set-selection 'SECONDARY (buffer-substring (point) (mark t))))
+
+(defun pgtk-paste-secondary ()
+  (interactive)
+  (insert (gui-get-selection 'SECONDARY)))
+
+
+;;;; Color support.
+
+;; Functions for color panel + drag
+(defun pgtk-face-at-pos (pos)
+  (let* ((frame (car pos))
+         (frame-pos (cons (cadr pos) (cddr pos)))
+         (window (window-at (car frame-pos) (cdr frame-pos) frame))
+         (window-pos (coordinates-in-window-p frame-pos window))
+         (buffer (window-buffer window))
+         (edges (window-edges window)))
+    (cond
+     ((not window-pos)
+      nil)
+     ((eq window-pos 'mode-line)
+      'mode-line)
+     ((eq window-pos 'vertical-line)
+      'default)
+     ((consp window-pos)
+      (with-current-buffer buffer
+        (let ((p (car (compute-motion (window-start window)
+                                      (cons (nth 0 edges) (nth 1 edges))
+                                      (window-end window)
+                                      frame-pos
+                                      (- (window-width window) 1)
+                                      nil
+                                      window))))
+          (cond
+           ((eq p (window-point window))
+            'cursor)
+           ((and mark-active (< (region-beginning) p) (< p (region-end)))
+            'region)
+           (t
+           (let ((faces (get-char-property p 'face window)))
+             (if (consp faces) (car faces) faces)))))))
+     (t
+      nil))))
+
+(defun pgtk-suspend-error ()
+  ;; Don't allow suspending if any of the frames are PGTK frames.
+  (if (memq 'pgtk (mapcar 'window-system (frame-list)))
+      (error "Cannot suspend Emacs while a PGTK GUI frame exists")))
+
+
+;; Set some options to be as Nextstep-like as possible.
+(setq frame-title-format t
+      icon-title-format t)
+
+
+(defvar pgtk-initialized nil
+  "Non-nil if pure-GTK windowing has been initialized.")
+
+(declare-function x-handle-args "common-win" (args))
+(declare-function x-open-connection "pgtkfns.c"
+                  (display &optional xrm-string must-succeed))
+(declare-function pgtk-set-resource "pgtkfns.c" (owner name value))
+
+;; Do the actual pure-GTK Windows setup here; the above code just
+;; defines functions and variables that we use now.
+(cl-defmethod window-system-initialization (&context (window-system pgtk)
+                                            &optional display)
+  "Initialize Emacs for pure-GTK windowing."
+  (cl-assert (not pgtk-initialized))
+
+  ;; PENDING: not needed?
+  (setq command-line-args (x-handle-args command-line-args))
+
+  ;; Make sure we have a valid resource name.
+  (or (stringp x-resource-name)
+      (let (i)
+       (setq x-resource-name (invocation-name))
+
+       ;; Change any . or * characters in x-resource-name to hyphens,
+       ;; so as not to choke when we use it in X resource queries.
+       (while (setq i (string-match "[.*]" x-resource-name))
+         (aset x-resource-name i ?-))))
+
+  ;; Setup the default fontset.
+  (create-default-fontset)
+  ;; Create the standard fontset.
+  (condition-case err
+      (create-fontset-from-fontset-spec pgtk-standard-fontset-spec t)
+    (error (display-warning
+            'initialization
+            (format "Creation of the standard fontset failed: %s" err)
+            :error)))
+
+  (x-open-connection (or display
+                         x-display-name)
+                    x-command-line-resources
+                    ;; Exit Emacs with fatal error if this fails and we
+                    ;; are the initial display.
+                     (= (length (frame-list)) 0))
+
+  (x-apply-session-resources)
+
+  ;; Don't let Emacs suspend under PGTK.
+  (add-hook 'suspend-hook 'pgtk-suspend-error)
+
+  (setq pgtk-initialized t))
+
+;; Any display name is OK.
+(add-to-list 'display-format-alist '(".*" . pgtk))
+(cl-defmethod handle-args-function (args &context (window-system pgtk))
+  (x-handle-args args))
+
+(cl-defmethod frame-creation-function (params &context (window-system pgtk))
+  (x-create-frame-with-faces params))
+
+(declare-function pgtk-own-selection-internal "pgtkselect.c" (selection value 
&optional frame))
+(declare-function pgtk-disown-selection-internal "pgtkselect.c" (selection 
&optional time_object terminal))
+(declare-function pgtk-selection-owner-p "pgtkselect.c" (&optional selection 
terminal))
+(declare-function pgtk-selection-exists-p "pgtkselect.c" (&optional selection 
terminal))
+(declare-function pgtk-get-selection-internal "pgtkselect.c" (selection-symbol 
target-type &optional time_stamp terminal))
+
+(cl-defmethod gui-backend-set-selection (selection value
+                                         &context (window-system pgtk))
+  (if value (pgtk-own-selection-internal selection value)
+    (pgtk-disown-selection-internal selection)))
+
+(cl-defmethod gui-backend-selection-owner-p (selection
+                                             &context (window-system pgtk))
+  (pgtk-selection-owner-p selection))
+
+(cl-defmethod gui-backend-selection-exists-p (selection
+                                              &context (window-system pgtk))
+  (pgtk-selection-exists-p selection))
+
+(cl-defmethod gui-backend-get-selection (selection-symbol target-type
+                                         &context (window-system pgtk))
+  (pgtk-get-selection-internal selection-symbol target-type))
+
+(provide 'pgtk-win)
+(provide 'term/pgtk-win)
+
+;;; pgtk-win.el ends here
diff --git a/lisp/url/url-privacy.el b/lisp/url/url-privacy.el
index 716e310..f1cbb26 100644
--- a/lisp/url/url-privacy.el
+++ b/lisp/url/url-privacy.el
@@ -46,6 +46,7 @@
          (pcase (or window-system 'tty)
            ('x "X11")
            ('ns "OpenStep")
+            ('pgtk "PureGTK")
            ('tty "TTY")
            (_ nil)))))
 
diff --git a/src/.gdbinit b/src/.gdbinit
index 78536fc..1efde9f 100644
--- a/src/.gdbinit
+++ b/src/.gdbinit
@@ -41,6 +41,9 @@ handle SIGUSR2 noprint pass
 # debugging.
 handle SIGALRM ignore
 
+# On selection send failed.
+handle SIGPIPE nostop noprint
+
 # Use $bugfix so that the value isn't a constant.
 # Using a constant runs into GDB bugs sometimes.
 define xgetptr
@@ -1224,6 +1227,7 @@ set print pretty on
 set print sevenbit-strings
 
 show environment DISPLAY
+show environment WAYLAND_DISPLAY
 show environment TERM
 
 # When debugging, it is handy to be able to "return" from
diff --git a/src/Makefile.in b/src/Makefile.in
index c5fb2ea..2c1e873 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -286,6 +286,9 @@ W32_OBJ=@W32_OBJ@
 ## -lkernel32 if CYGWIN but not HAVE_W32, else empty.
 W32_LIBS=@W32_LIBS@
 
+PGTK_OBJ=@PGTK_OBJ@
+PGTK_LIBS=@PGTK_LIBS@
+
 ## emacs.res if HAVE_W32
 EMACSRES = @EMACSRES@
 ## If HAVE_W32, compiler arguments for including
@@ -421,7 +424,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o 
$(XMENU_OBJ) window.o \
        profiler.o decompress.o \
        thread.o systhread.o \
        $(if $(HYBRID_MALLOC),sheap.o) \
-       $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \
+       $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(PGTK_OBJ) $(CYGWIN_OBJ) 
$(FONT_OBJ) \
        $(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) $(JSON_OBJ) $(GMP_OBJ)
 obj = $(base_obj) $(NS_OBJC_OBJ)
 
@@ -519,7 +522,7 @@ shortlisp := loaddefs.el loadup.el $(sort ${shortlisp})
 lisp = $(addprefix ${lispsource}/,${shortlisp})
 
 ## Construct full set of libraries to be linked.
-LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \
+LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(PGTK_LIBS) $(LIBX_BASE) 
$(LIBIMAGE) \
    $(LIBX_OTHER) $(LIBSOUND) \
    $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_ACL) $(LIB_CLOCK_GETTIME) \
    $(WEBKIT_LIBS) \
diff --git a/src/alloc.c b/src/alloc.c
index 2b3643e..0dc7f7f 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -96,7 +96,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <unistd.h>
 #include <fcntl.h>
 
-#ifdef USE_GTK
+#if defined(USE_GTK)
 # include "gtkutil.h"
 #endif
 #ifdef WINDOWSNT
@@ -6052,8 +6052,11 @@ garbage_collect (void)
   mark_terminals ();
   mark_kboards ();
   mark_threads ();
+#ifdef HAVE_PGTK
+  mark_pgtkterm();
+#endif
 
-#ifdef USE_GTK
+#if defined(USE_GTK)
   xg_mark_data ();
 #endif
 
diff --git a/src/dispextern.h b/src/dispextern.h
index da51772..35e1210 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -134,6 +134,14 @@ typedef Emacs_Pixmap Emacs_Pix_Context;
 #define FACE_COLOR_TO_PIXEL(face_color, frame) face_color
 #endif
 
+#ifdef HAVE_PGTK
+#include "pgtkgui.h"
+/* Following typedef needed to accommodate the MSDOS port, believe it or not.  
*/
+typedef struct pgtk_display_info Display_Info;
+typedef Emacs_Pixmap XImagePtr;
+typedef XImagePtr XImagePtr_or_DC;
+#endif
+
 #ifdef HAVE_WINDOW_SYSTEM
 # include <time.h>
 # include "fontset.h"
@@ -1393,6 +1401,9 @@ struct glyph_string
   Emacs_GC *gc;
   HDC hdc;
 #endif
+#if defined (HAVE_PGTK)
+  XGCValues xgcv;
+#endif
 
   /* A pointer to the first glyph in the string.  This glyph
      corresponds to char2b[0].  Needed to draw rectangles if
diff --git a/src/dispnew.c b/src/dispnew.c
index 89dd32a..1aee18e 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -6403,6 +6403,19 @@ init_display_interactive (void)
     }
 #endif
 
+#ifdef HAVE_PGTK
+  if (!inhibit_window_system
+#ifndef CANNOT_DUMP
+     && initialized
+#endif
+      )
+    {
+      Vinitial_window_system = Qpgtk;
+      Vwindow_system_version = make_fixnum (1);
+      return;
+    }
+#endif
+
   /* If no window system has been specified, try to use the terminal.  */
   if (! isatty (STDIN_FILENO))
     fatal ("standard input is not a tty");
diff --git a/src/emacs.c b/src/emacs.c
index 172e460..769a852 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1586,6 +1586,9 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
   init_bignum ();
   init_threads ();
   init_eval ();
+#ifdef HAVE_PGTK
+  init_pgtkterm ();   /* before init_atimer(). */
+#endif
   init_atimer ();
   running_asynch_code = 0;
   init_random ();
@@ -1914,6 +1917,14 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
       syms_of_nsselect ();
       syms_of_fontset ();
 #endif /* HAVE_NS */
+#ifdef HAVE_PGTK
+      syms_of_pgtkterm();
+      syms_of_pgtkfns();
+      syms_of_pgtkselect ();
+      syms_of_fontset ();
+      syms_of_xsettings ();
+      syms_of_xwidget ();
+#endif
 
       syms_of_gnutls ();
 
@@ -1982,9 +1993,11 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
 #ifdef HAVE_DBUS
   init_dbusbind ();
 #endif
-#ifdef USE_GTK
+#if defined(USE_GTK)
+#ifndef HAVE_PGTK
   init_xterm ();
 #endif
+#endif
 
   /* This can create a thread that may call getenv, so it must follow
      all calls to putenv and setenv.  Also, this sets up
diff --git a/src/emacsgtkfixed.c b/src/emacsgtkfixed.c
index ea9465d..aeca3d4 100644
--- a/src/emacsgtkfixed.c
+++ b/src/emacsgtkfixed.c
@@ -22,7 +22,11 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 #include "lisp.h"
 #include "frame.h"
+#ifdef HAVE_PGTK
+#include "pgtkterm.h"
+#else
 #include "xterm.h"
+#endif
 #include "xwidget.h"
 #include "emacsgtkfixed.h"
 
@@ -182,7 +186,11 @@ emacs_fixed_get_preferred_width (GtkWidget *widget,
 {
   EmacsFixed *fixed = EMACS_FIXED (widget);
   EmacsFixedPrivate *priv = fixed->priv;
+#ifdef HAVE_PGTK
+  int w = priv->f->output_data.pgtk->size_hints.min_width;
+#else
   int w = priv->f->output_data.x->size_hints.min_width;
+#endif
   if (minimum) *minimum = w;
   if (natural) *natural = w;
 }
@@ -194,12 +202,18 @@ emacs_fixed_get_preferred_height (GtkWidget *widget,
 {
   EmacsFixed *fixed = EMACS_FIXED (widget);
   EmacsFixedPrivate *priv = fixed->priv;
+#ifdef HAVE_PGTK
+  int h = priv->f->output_data.pgtk->size_hints.min_height;
+#else
   int h = priv->f->output_data.x->size_hints.min_height;
+#endif
   if (minimum) *minimum = h;
   if (natural) *natural = h;
 }
 
 
+#ifndef HAVE_PGTK
+
 /* Override the X function so we can intercept Gtk+ 3 calls.
    Use our values for min_width/height so that KDE don't freak out
    (Bug#8919), and so users can resize our frames as they wish.  */
@@ -234,8 +248,13 @@ XSetWMSizeHints (Display *d,
 
   if ((hints->flags & PMinSize) && f)
     {
+#ifdef HAVE_PGTK
+      int w = f->output_data.pgtk->size_hints.min_width;
+      int h = f->output_data.pgtk->size_hints.min_height;
+#else
       int w = f->output_data.x->size_hints.min_width;
       int h = f->output_data.x->size_hints.min_height;
+#endif
       data[5] = w;
       data[6] = h;
     }
@@ -253,3 +272,5 @@ XSetWMNormalHints (Display *d, Window w, XSizeHints *hints)
 {
   XSetWMSizeHints (d, w, hints, XA_WM_NORMAL_HINTS);
 }
+
+#endif
diff --git a/src/font.c b/src/font.c
index 8dbf8cb..52c1cb8 100644
--- a/src/font.c
+++ b/src/font.c
@@ -5567,7 +5567,11 @@ match.  */);
   syms_of_xftfont ();
 #endif  /* HAVE_XFT */
 #endif  /* not USE_CAIRO */
-#endif /* HAVE_X_WINDOWS */
+#else  /* not HAVE_X_WINDOWS */
+#ifdef USE_CAIRO
+  syms_of_ftcrfont ();
+#endif
+#endif /* not HAVE_X_WINDOWS */
 #else  /* not HAVE_FREETYPE */
 #ifdef HAVE_X_WINDOWS
   syms_of_xfont ();
diff --git a/src/font.h b/src/font.h
index 8614e7f..89c1a7a 100644
--- a/src/font.h
+++ b/src/font.h
@@ -822,7 +822,7 @@ extern Lisp_Object merge_font_spec (Lisp_Object, 
Lisp_Object);
 
 extern Lisp_Object font_make_entity (void);
 extern Lisp_Object font_make_object (int, Lisp_Object, int);
-#if defined (HAVE_XFT) || defined (HAVE_FREETYPE) || defined (HAVE_NS)
+#if defined (HAVE_XFT) || defined (HAVE_FREETYPE) || defined (HAVE_NS) || 
defined(HAVE_PGTK)
 extern Lisp_Object font_build_object (int, Lisp_Object, Lisp_Object, double);
 #endif
 
diff --git a/src/frame.c b/src/frame.c
index 512aaf5..2c549a1 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -291,6 +291,8 @@ See also `frame-live-p'.  */)
       return Qpc;
     case output_ns:
       return Qns;
+    case output_pgtk:
+      return Qpgtk;
     default:
       emacs_abort ();
     }
@@ -2149,7 +2151,8 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
     /* Since a similar behavior was observed on the Lucid and Motif
        builds (see Bug#5802, Bug#21509, Bug#23499, Bug#27816), we now
        don't delete the terminal for these builds either.  */
-    if (terminal->reference_count == 0 && terminal->type == output_x_window)
+    if (terminal->reference_count == 0 &&
+       (terminal->type == output_x_window || terminal->type == output_pgtk))
       terminal->reference_count = 1;
 #endif /* USE_X_TOOLKIT || USE_GTK */
     if (terminal->reference_count == 0)
@@ -5749,7 +5752,7 @@ selected frame.  This is useful when 
`make-pointer-invisible' is set.  */)
 
 #ifdef HAVE_WINDOW_SYSTEM
 
-# if (defined USE_GTK || defined HAVE_NS || defined HAVE_XINERAMA \
+# if (defined USE_GTK || defined HAVE_PGTK || defined HAVE_NS || defined 
HAVE_XINERAMA \
       || defined HAVE_XRANDR)
 void
 free_monitors (struct MonitorInfo *monitors, int n_monitors)
@@ -5876,6 +5879,7 @@ syms_of_frame (void)
   DEFSYM (Qw32, "w32");
   DEFSYM (Qpc, "pc");
   DEFSYM (Qns, "ns");
+  DEFSYM (Qpgtk, "pgtk");
   DEFSYM (Qvisible, "visible");
   DEFSYM (Qbuffer_predicate, "buffer-predicate");
   DEFSYM (Qbuffer_list, "buffer-list");
diff --git a/src/frame.h b/src/frame.h
index 16ecfd3..d8f7e4b 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -574,6 +574,7 @@ struct frame
     struct x_output *x;         /* From xterm.h.  */
     struct w32_output *w32;     /* From w32term.h.  */
     struct ns_output *ns;       /* From nsterm.h.  */
+    struct pgtk_output *pgtk; /* From pgtkterm.h. */
   }
   output_data;
 
@@ -841,6 +842,11 @@ default_pixels_per_inch_y (void)
 #else
 #define FRAME_NS_P(f) ((f)->output_method == output_ns)
 #endif
+#ifndef HAVE_PGTK
+#define FRAME_PGTK_P(f) false
+#else
+#define FRAME_PGTK_P(f) ((f)->output_method == output_pgtk)
+#endif
 
 /* FRAME_WINDOW_P tests whether the frame is a graphical window system
    frame.  */
@@ -854,6 +860,9 @@ default_pixels_per_inch_y (void)
 #ifdef HAVE_NS
 #define FRAME_WINDOW_P(f) FRAME_NS_P(f)
 #endif
+#ifdef HAVE_PGTK
+#define FRAME_WINDOW_P(f) FRAME_PGTK_P(f)
+#endif
 #ifndef FRAME_WINDOW_P
 #define FRAME_WINDOW_P(f) ((void) (f), false)
 #endif
@@ -1701,7 +1710,7 @@ extern const char *x_get_resource_string (const char *, 
const char *);
 extern void x_sync (struct frame *);
 #endif /* HAVE_X_WINDOWS */
 
-#ifndef HAVE_NS
+#if !defined(HAVE_NS) && !defined(HAVE_PGTK)
 
 /* Set F's bitmap icon, if specified among F's parameters.  */
 
diff --git a/src/fringe.c b/src/fringe.c
index 7549669..6259894 100644
--- a/src/fringe.c
+++ b/src/fringe.c
@@ -30,6 +30,8 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "termhooks.h"
 #include "pdumper.h"
 
+#include "pgtkterm.h"
+
 /* Fringe bitmaps are represented in three different ways:
 
    Logical bitmaps are used internally to denote things like
@@ -1398,7 +1400,7 @@ If BITMAP overrides a standard fringe bitmap, the 
original bitmap is restored.
    On W32 and MAC (little endian), there's no need to do this.
 */
 
-#if defined (HAVE_X_WINDOWS)
+#if defined (HAVE_X_WINDOWS) || defined(HAVE_PGTK)
 static const unsigned char swap_nibble[16] = {
   0x0, 0x8, 0x4, 0xc,           /* 0000 1000 0100 1100 */
   0x2, 0xa, 0x6, 0xe,           /* 0010 1010 0110 1110 */
@@ -1461,6 +1463,25 @@ init_fringe_bitmap (int which, struct fringe_bitmap *fb, 
int once_p)
 #endif /* not USE_CAIRO */
 #endif /* HAVE_X_WINDOWS */
 
+#if !defined(HAVE_X_WINDOWS) && defined (HAVE_PGTK)
+      unsigned short *bits = fb->bits;
+      int j;
+
+      for (j = 0; j < fb->height; j++)
+       {
+         unsigned short b = *bits;
+#ifdef WORDS_BIGENDIAN
+         *bits++ = (b << (16 - fb->width));
+#else
+         b = (unsigned short)((swap_nibble[b & 0xf] << 12)
+                              | (swap_nibble[(b>>4) & 0xf] << 8)
+                              | (swap_nibble[(b>>8) & 0xf] << 4)
+                              | (swap_nibble[(b>>12) & 0xf]));
+         *bits++ = (b >> (16 - fb->width));
+#endif
+       }
+#endif /* !HAVE_X_WINDOWS && HAVE_PGTK */
+
 #ifdef HAVE_NTGUI
       unsigned short *bits = fb->bits;
       int j;
diff --git a/src/ftcrfont.c b/src/ftcrfont.c
index b895107..8fffb33 100644
--- a/src/ftcrfont.c
+++ b/src/ftcrfont.c
@@ -22,7 +22,11 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <cairo-ft.h>
 
 #include "lisp.h"
+#ifndef HAVE_PGTK
 #include "xterm.h"
+#else
+#include "pgtkterm.h"
+#endif
 #include "blockinput.h"
 #include "charset.h"
 #include "composite.h"
@@ -513,11 +517,19 @@ ftcrfont_draw (struct glyph_string *s,
 
   block_input ();
 
+#ifndef HAVE_PGTK
   cr = x_begin_cr_clip (f, s->gc);
+#else
+  cr = pgtk_begin_cr_clip (f);
+#endif
 
   if (with_background)
     {
+#ifndef HAVE_PGTK
       x_set_cr_source_with_gc_background (f, s->gc);
+#else
+      pgtk_set_cr_source_with_color (f, s->xgcv.background);
+#endif
       cairo_rectangle (cr, x, y - FONT_BASE (face->font),
                       s->width, FONT_HEIGHT (face->font));
       cairo_fill (cr);
@@ -534,11 +546,19 @@ ftcrfont_draw (struct glyph_string *s,
                                                        NULL));
     }
 
+#ifndef HAVE_PGTK
   x_set_cr_source_with_gc_foreground (f, s->gc);
+#else
+  pgtk_set_cr_source_with_color (f, s->xgcv.foreground);
+#endif
   cairo_set_scaled_font (cr, ftcrfont_info->cr_scaled_font);
   cairo_show_glyphs (cr, glyphs, len);
 
+#ifndef HAVE_PGTK
   x_end_cr_clip (f);
+#else
+  pgtk_end_cr_clip (f);
+#endif
 
   unblock_input ();
 
diff --git a/src/ftfont.h b/src/ftfont.h
index f771dc1..94050dd 100644
--- a/src/ftfont.h
+++ b/src/ftfont.h
@@ -25,6 +25,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #include FT_SIZES_H
+#include FT_TRUETYPE_TABLES_H
 #ifdef FT_BDF_H
 # include FT_BDF_H
 #endif
diff --git a/src/gtkutil.c b/src/gtkutil.c
index fafd94c..b7a397b 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -26,7 +26,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 #include <config.h>
 
-#ifdef USE_GTK
+#if defined(USE_GTK)
 #include <float.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -37,13 +37,24 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "dispextern.h"
 #include "frame.h"
 #include "systime.h"
+#ifndef HAVE_PGTK
 #include "xterm.h"
+#define xp x
+typedef struct x_output xp_output;
+#else
+#define xp pgtk
+typedef struct pgtk_output xp_output;
+#endif
 #include "blockinput.h"
 #include "window.h"
 #include "gtkutil.h"
 #include "termhooks.h"
 #include "keyboard.h"
 #include "coding.h"
+#ifndef PGTK_TRACE
+#define PGTK_TRACE(fmt, ...) ((void) 0)
+#define PGTK_BACKTRACE() ((void) 0)
+#endif
 
 #include <gdk/gdkkeysyms.h>
 
@@ -52,7 +63,9 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #endif
 
 #ifdef HAVE_GTK3
+#ifndef HAVE_PGTK
 #include <gtk/gtkx.h>
+#endif
 #include "emacsgtkfixed.h"
 #endif
 
@@ -129,6 +142,7 @@ static GdkDisplay *gdpy_def;
 static void
 xg_set_screen (GtkWidget *w, struct frame *f)
 {
+#ifndef HAVE_PGTK
   if (FRAME_X_DISPLAY (f) != DEFAULT_GDK_DISPLAY ())
     {
       GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
@@ -139,6 +153,17 @@ xg_set_screen (GtkWidget *w, struct frame *f)
       else
         gtk_window_set_screen (GTK_WINDOW (w), gscreen);
     }
+#else
+  if (FRAME_X_DISPLAY(f) != DEFAULT_GDK_DISPLAY ())
+    {
+      GdkScreen *gscreen = gdk_display_get_default_screen (FRAME_X_DISPLAY(f));
+
+      if (GTK_IS_MENU (w))
+       gtk_menu_set_screen (GTK_MENU (w), gscreen);
+      else
+       gtk_window_set_screen (GTK_WINDOW (w), gscreen);
+    }
+#endif
 }
 
 
@@ -150,12 +175,20 @@ xg_set_screen (GtkWidget *w, struct frame *f)
    multiple displays.  */
 
 void
+#ifndef HAVE_PGTK
 xg_display_open (char *display_name, Display **dpy)
+#else
+xg_display_open (char *display_name, GdkDisplay **dpy)
+#endif
 {
   GdkDisplay *gdpy;
 
   unrequest_sigio ();  /* See comment in x_display_ok, xterm.c.  */
+#ifndef HAVE_PGTK
   gdpy = gdk_display_open (display_name);
+#else
+  gdpy = gdk_display_open (strlen(display_name) == 0 ? NULL : display_name);
+#endif
   request_sigio ();
   if (!gdpy_def && gdpy)
     {
@@ -164,7 +197,11 @@ xg_display_open (char *display_name, Display **dpy)
                                               gdpy);
     }
 
+#ifndef HAVE_PGTK
   *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
+#else
+  *dpy = gdpy;
+#endif
 }
 
 /* Scaling/HiDPI functions. */
@@ -196,8 +233,13 @@ xg_get_scale (struct frame *f)
 /* Close display DPY.  */
 
 void
+#ifndef HAVE_PGTK
 xg_display_close (Display *dpy)
+#else
+xg_display_close (GdkDisplay *gdpy)
+#endif
 {
+#ifndef HAVE_PGTK
   GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
 
   /* If this is the default display, try to change it before closing.
@@ -221,6 +263,31 @@ xg_display_close (Display *dpy)
     }
 
   gdk_display_close (gdpy);
+
+#else
+
+  /* If this is the default display, try to change it before closing.
+     If there is no other display to use, gdpy_def is set to NULL, and
+     the next call to xg_display_open resets the default display.  */
+  if (gdk_display_get_default () == gdpy)
+    {
+      struct pgtk_display_info *dpyinfo;
+      GdkDisplay *gdpy_new = NULL;
+
+      /* Find another display.  */
+      for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
+       if (dpyinfo->gdpy != gdpy)
+         {
+           gdpy_new = dpyinfo->gdpy;
+           gdk_display_manager_set_default_display (gdk_display_manager_get (),
+                                                    gdpy_new);
+            break;
+        }
+      gdpy_def = gdpy_new;
+    }
+
+  gdk_display_close (gdpy);
+#endif
 }
 
 
@@ -232,12 +299,19 @@ xg_display_close (Display *dpy)
    scroll bars on display DPY.  */
 
 GdkCursor *
+#ifndef HAVE_PGTK
 xg_create_default_cursor (Display *dpy)
+#else
+xg_create_default_cursor (GdkDisplay *gdpy)
+#endif
 {
+#ifndef HAVE_PGTK
   GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
+#endif
   return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
 }
 
+#ifndef HAVE_PGTK
 /* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel.  */
 
 static GdkPixbuf *
@@ -250,8 +324,10 @@ xg_get_pixbuf_from_pix_and_mask (struct frame *f,
   Window wunused;
   unsigned int width, height, depth, uunused;
 
+#ifndef HAVE_PGTK
   if (FRAME_DISPLAY_INFO (f)->red_bits != 8)
     return 0;
+#endif
   XGetGeometry (FRAME_X_DISPLAY (f), pix, &wunused, &iunused, &iunused,
                 &width, &height, &uunused, &depth);
   if (depth != 24)
@@ -286,6 +362,7 @@ xg_get_pixbuf_from_pix_and_mask (struct frame *f,
 
   return icon_buf;
 }
+#endif
 
 #if defined USE_CAIRO && !defined HAVE_GTK3
 static GdkPixbuf *
@@ -633,8 +710,12 @@ xg_check_special_colors (struct frame *f,
       g = col.green * 65535,
       b = col.blue * 65535;
     sprintf (buf, "rgb:%04x/%04x/%04x", r, g, b);
+#ifndef HAVE_PGTK
     success_p = x_parse_color (f, buf, color) != 0;
 #else
+    success_p = pgtk_parse_color (buf, color) != 0;
+#endif
+#else
     GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f));
     GdkColor *grgb = get_bg
       ? &gsty->bg[GTK_STATE_SELECTED]
@@ -667,7 +748,7 @@ hierarchy_ch_cb (GtkWidget *widget,
                  gpointer   user_data)
 {
   struct frame *f = user_data;
-  struct x_output *x = f->output_data.x;
+  xp_output *x = f->output_data.xp;
   GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl);
 
   if (! top || ! GTK_IS_WINDOW (top))
@@ -689,7 +770,7 @@ qttip_cb (GtkWidget  *widget,
           gpointer    user_data)
 {
   struct frame *f = user_data;
-  struct x_output *x = f->output_data.x;
+  xp_output *x = f->output_data.xp;
   if (x->ttip_widget == NULL)
     {
       GtkWidget *p;
@@ -736,7 +817,7 @@ xg_prepare_tooltip (struct frame *f,
                     int *width,
                     int *height)
 {
-  struct x_output *x = f->output_data.x;
+  xp_output *x = f->output_data.xp;
   GtkWidget *widget;
   GdkWindow *gwin;
   GdkScreen *screen;
@@ -787,13 +868,19 @@ xg_prepare_tooltip (struct frame *f,
 void
 xg_show_tooltip (struct frame *f, int root_x, int root_y)
 {
-  struct x_output *x = f->output_data.x;
+  xp_output *x = f->output_data.xp;
   if (x->ttip_window)
     {
       block_input ();
+#ifndef HAVE_PGTK
       gtk_window_move (x->ttip_window, root_x / xg_get_scale (f),
                       root_y / xg_get_scale (f));
       gtk_widget_show (GTK_WIDGET (x->ttip_window));
+#else
+      gtk_widget_show (GTK_WIDGET (x->ttip_window));
+      gtk_window_move (x->ttip_window, root_x / xg_get_scale (f),
+                      root_y / xg_get_scale (f));
+#endif
       unblock_input ();
     }
 }
@@ -805,10 +892,10 @@ xg_show_tooltip (struct frame *f, int root_x, int root_y)
 bool
 xg_hide_tooltip (struct frame *f)
 {
-  if (f->output_data.x->ttip_window)
+  bool ret = 0;
+  if (f->output_data.xp->ttip_window)
     {
-      GtkWindow *win = f->output_data.x->ttip_window;
-
+      GtkWindow *win = f->output_data.xp->ttip_window;
       block_input ();
       gtk_widget_hide (GTK_WIDGET (win));
 
@@ -932,10 +1019,16 @@ xg_frame_resized (struct frame *f, int pixelwidth, int 
pixelheight)
 
   width = FRAME_PIXEL_TO_TEXT_WIDTH (f, pixelwidth);
   height = FRAME_PIXEL_TO_TEXT_HEIGHT (f, pixelheight);
+  PGTK_TRACE("xg_frame_resized: pixel: %dx%d, text: %dx%d", pixelwidth, 
pixelheight, width, height);
 
   frame_size_history_add
     (f, Qxg_frame_resized, width, height, Qnil);
 
+  PGTK_TRACE("width: %d -> %d.", FRAME_TEXT_WIDTH(f), width);
+  PGTK_TRACE("height: %d -> %d.", FRAME_TEXT_HEIGHT(f), height);
+  PGTK_TRACE("pixelwidth: %d -> %d.", FRAME_PIXEL_WIDTH(f), pixelwidth);
+  PGTK_TRACE("pixelheight: %d -> %d.", FRAME_PIXEL_HEIGHT(f), pixelheight);
+
   if (width != FRAME_TEXT_WIDTH (f)
       || height != FRAME_TEXT_HEIGHT (f)
       || pixelwidth != FRAME_PIXEL_WIDTH (f)
@@ -945,6 +1038,9 @@ xg_frame_resized (struct frame *f, int pixelwidth, int 
pixelheight)
       change_frame_size (f, width, height, 0, 1, 0, 1);
       SET_FRAME_GARBAGED (f);
       cancel_mouse_face (f);
+#ifdef HAVE_PGTK
+      pgtk_cr_destroy_surface (f);
+#endif
     }
 }
 
@@ -1007,7 +1103,11 @@ xg_frame_set_char_size (struct frame *f, int width, int 
height)
   else if (FRAME_PARENT_FRAME (f) && FRAME_VISIBLE_P (f))
     {
       was_visible = true;
+#ifndef HAVE_PGTK
       hide_child_frame = EQ (x_gtk_resize_child_frames, Qhide);
+#else
+      hide_child_frame = true;
+#endif // !HAVE_PGTK
 
       if (totalwidth != gwidth || totalheight != gheight)
        {
@@ -1060,7 +1160,9 @@ xg_frame_set_char_size (struct frame *f, int width, int 
height)
       /* Must call this to flush out events */
       (void)gtk_events_pending ();
       gdk_flush ();
+#ifndef HAVE_PGTK
       x_wait_for_event (f, ConfigureNotify);
+#endif
 
       if (!NILP (fullscreen))
        /* Try to restore fullscreen state.  */
@@ -1071,24 +1173,24 @@ xg_frame_set_char_size (struct frame *f, int width, int 
height)
     }
   else
     adjust_frame_size (f, width, height, 5, 0, Qxg_frame_set_char_size);
-
 }
 
+#ifndef HAVE_PGTK
 /* Handle height/width changes (i.e. add/remove/move menu/toolbar).
    The policy is to keep the number of editable lines.  */
 
-#if 0
 static void
 xg_height_or_width_changed (struct frame *f)
 {
   gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
                      FRAME_TOTAL_PIXEL_WIDTH (f),
                      FRAME_TOTAL_PIXEL_HEIGHT (f));
-  f->output_data.x->hint_flags = 0;
+  f->output_data.xp->hint_flags = 0;
   x_wm_set_size_hint (f, 0, 0);
 }
 #endif
 
+#ifndef HAVE_PGTK
 /* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
    Must be done like this, because GtkWidget:s can have "hidden"
    X Window that aren't accessible.
@@ -1116,6 +1218,7 @@ xg_win_to_widget (Display *dpy, Window wdesc)
   unblock_input ();
   return gwdesc;
 }
+#endif
 
 /* Set the background of widget W to PIXEL.  */
 
@@ -1123,9 +1226,18 @@ static void
 xg_set_widget_bg (struct frame *f, GtkWidget *w, unsigned long pixel)
 {
 #ifdef HAVE_GTK3
-  XColor xbg;
+  Emacs_Color xbg;
   xbg.pixel = pixel;
+#ifndef HAVE_PGTK
   if (XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &xbg))
+#else
+  xbg.red = (pixel >> 16) & 0xff;
+  xbg.green = (pixel >> 8) & 0xff;
+  xbg.blue = (pixel >> 0) & 0xff;
+  xbg.red |= xbg.red << 8;
+  xbg.green |= xbg.green << 8;
+  xbg.blue |= xbg.blue << 8;
+#endif
     {
       const char format[] = "* { background-color: #%02x%02x%02x; }";
       /* The format is always longer than the resulting string.  */
@@ -1160,7 +1272,16 @@ style_changed_cb (GObject *go,
   struct input_event event;
   GdkDisplay *gdpy = user_data;
   const char *display_name = gdk_display_get_name (gdpy);
+#ifndef HAVE_PGTK
   Display *dpy = GDK_DISPLAY_XDISPLAY (gdpy);
+#else
+  GdkDisplay *dpy = gdpy;
+#endif
+
+#ifndef HAVE_PGTK
+  if (display_name == NULL)
+    display_name = "";
+#endif
 
   EVENT_INIT (event);
   event.kind = CONFIG_CHANGED_EVENT;
@@ -1181,7 +1302,11 @@ style_changed_cb (GObject *go,
         {
           struct frame *f = XFRAME (frame);
           if (FRAME_LIVE_P (f)
+#ifndef HAVE_PGTK
               && FRAME_X_P (f)
+#else
+              && FRAME_PGTK_P (f)
+#endif
               && FRAME_X_DISPLAY (f) == dpy)
             {
               FRAME_TERMINAL (f)->set_scroll_bar_default_width_hook (f);
@@ -1216,15 +1341,21 @@ xg_create_frame_widgets (struct frame *f)
 #endif
   char *title = 0;
 
+  PGTK_TRACE("xg_create_frame_widgets.");
   block_input ();
 
+#ifndef HAVE_PGTK  // gtk_plug not found.
   if (FRAME_X_EMBEDDED_P (f))
     {
       GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
-      wtop = gtk_plug_new_for_display (gdpy, f->output_data.x->parent_desc);
+      wtop = gtk_plug_new_for_display (gdpy, f->output_data.xp->parent_desc);
     }
   else
+#endif
     wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+#ifdef HAVE_PGTK
+  gtk_widget_add_events(wtop, GDK_ALL_EVENTS_MASK);
+#endif
 
   /* gtk_window_set_has_resize_grip is a Gtk+ 3.0 function but Ubuntu
      has backported it to Gtk+ 2.0 and they add the resize grip for
@@ -1281,8 +1412,8 @@ xg_create_frame_widgets (struct frame *f)
 
   FRAME_GTK_OUTER_WIDGET (f) = wtop;
   FRAME_GTK_WIDGET (f) = wfixed;
-  f->output_data.x->vbox_widget = wvbox;
-  f->output_data.x->hbox_widget = whbox;
+  f->output_data.xp->vbox_widget = wvbox;
+  f->output_data.xp->hbox_widget = whbox;
 
   gtk_widget_set_has_window (wfixed, TRUE);
 
@@ -1309,10 +1440,12 @@ xg_create_frame_widgets (struct frame *f)
                           SSDATA (Vx_resource_class));
 #endif
 
+#ifndef HAVE_PGTK
   /* Add callback to do nothing on WM_DELETE_WINDOW.  The default in
      GTK is to destroy the widget.  We want Emacs to do that instead.  */
   g_signal_connect (G_OBJECT (wtop), "delete-event",
                     G_CALLBACK (delete_cb), f);
+#endif
 
   /* Convert our geometry parameters into a geometry string
      and specify it.
@@ -1323,7 +1456,9 @@ xg_create_frame_widgets (struct frame *f)
 
   gtk_widget_add_events (wfixed,
                          GDK_POINTER_MOTION_MASK
+#ifndef HAVE_PGTK
                          | GDK_EXPOSURE_MASK
+#endif
                          | GDK_BUTTON_PRESS_MASK
                          | GDK_BUTTON_RELEASE_MASK
                          | GDK_KEY_PRESS_MASK
@@ -1331,13 +1466,25 @@ xg_create_frame_widgets (struct frame *f)
                          | GDK_LEAVE_NOTIFY_MASK
                          | GDK_FOCUS_CHANGE_MASK
                          | GDK_STRUCTURE_MASK
+#ifdef HAVE_PGTK
+                         | GDK_SCROLL_MASK
+                         | GDK_SMOOTH_SCROLL_MASK
+#endif
                          | GDK_VISIBILITY_NOTIFY_MASK);
 
   /* Must realize the windows so the X window gets created.  It is used
      by callers of this function.  */
+#ifndef HAVE_PGTK
   gtk_widget_realize (wfixed);
+#else
+  gtk_widget_show_all(wtop);
+#endif
+#ifndef HAVE_PGTK
   FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
+#endif
+#ifndef HAVE_PGTK
   initial_set_up_x_back_buffer (f);
+#endif
 
   /* Since GTK clears its window by filling with the background color,
      we must keep X and GTK background in sync.  */
@@ -1354,6 +1501,7 @@ xg_create_frame_widgets (struct frame *f)
   gtk_widget_modify_style (wfixed, style);
 #else
   gtk_widget_set_can_focus (wfixed, TRUE);
+  gtk_widget_grab_focus(wfixed);
   gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE);
 #endif
 
@@ -1366,9 +1514,9 @@ xg_create_frame_widgets (struct frame *f)
     }
 
   /* Steal a tool tip window we can move ourselves.  */
-  f->output_data.x->ttip_widget = 0;
-  f->output_data.x->ttip_lbl = 0;
-  f->output_data.x->ttip_window = 0;
+  f->output_data.xp->ttip_widget = 0;
+  f->output_data.xp->ttip_lbl = 0;
+  f->output_data.xp->ttip_window = 0;
   gtk_widget_set_tooltip_text (wtop, "Dummy text");
   g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f);
 
@@ -1398,7 +1546,7 @@ xg_free_frame_widgets (struct frame *f)
 {
   if (FRAME_GTK_OUTER_WIDGET (f))
     {
-      struct x_output *x = f->output_data.x;
+      xp_output *x = f->output_data.xp;
       struct xg_frame_tb_info *tbinfo
         = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
                              TB_INFO_KEY);
@@ -1406,10 +1554,14 @@ xg_free_frame_widgets (struct frame *f)
         xfree (tbinfo);
 
       /* x_free_frame_resources should have taken care of it */
+#ifndef HAVE_PGTK
       eassert (!FRAME_X_DOUBLE_BUFFERED_P (f));
+#endif
       gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
       FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
+#ifndef HAVE_PGTK
       FRAME_X_RAW_DRAWABLE (f) = 0;
+#endif
       FRAME_GTK_OUTER_WIDGET (f) = 0;
       if (x->ttip_widget)
         {
@@ -1451,6 +1603,7 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool 
user_position)
 
   XSETFRAME (frame, f);
   fs_state = Fframe_parameter (frame, Qfullscreen);
+#ifndef HAVE_PGTK
   if ((EQ (fs_state, Qmaximized) || EQ (fs_state, Qfullboth)) &&
       (x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state) ||
        x_wm_supports (f, FRAME_DISPLAY_INFO 
(f)->Xatom_net_wm_state_fullscreen)))
@@ -1460,18 +1613,19 @@ x_wm_set_size_hint (struct frame *f, long int flags, 
bool user_position)
       */
       return;
     }
+#endif
 
   if (flags)
     {
       memset (&size_hints, 0, sizeof (size_hints));
-      f->output_data.x->size_hints = size_hints;
-      f->output_data.x->hint_flags = hint_flags;
+      f->output_data.xp->size_hints = size_hints;
+      f->output_data.xp->hint_flags = hint_flags;
     }
   else
     flags = f->size_hint_flags;
 
-  size_hints = f->output_data.x->size_hints;
-  hint_flags = f->output_data.x->hint_flags;
+  size_hints = f->output_data.xp->size_hints;
+  hint_flags = f->output_data.xp->hint_flags;
 
   hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
   size_hints.width_inc = frame_resize_pixelwise ? 1 : FRAME_COLUMN_WIDTH (f);
@@ -1484,6 +1638,7 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool 
user_position)
   base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 1) + FRAME_TOOLBAR_WIDTH (f);
   base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 1)
     + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f);
+  PGTK_TRACE("base: %dx%d\n", base_width, base_height);
 
   size_hints.base_width = base_width;
   size_hints.base_height = base_height;
@@ -1533,16 +1688,16 @@ x_wm_set_size_hint (struct frame *f, long int flags, 
bool user_position)
   size_hints.width_inc /= scale;
   size_hints.height_inc /= scale;
 
-  if (hint_flags != f->output_data.x->hint_flags
+  if (hint_flags != f->output_data.xp->hint_flags
       || memcmp (&size_hints,
-                &f->output_data.x->size_hints,
+                &f->output_data.xp->size_hints,
                 sizeof (size_hints)) != 0)
     {
       block_input ();
       gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
                                      NULL, &size_hints, hint_flags);
-      f->output_data.x->size_hints = size_hints;
-      f->output_data.x->hint_flags = hint_flags;
+      f->output_data.xp->size_hints = size_hints;
+      f->output_data.xp->hint_flags = hint_flags;
       unblock_input ();
     }
 }
@@ -1608,7 +1763,11 @@ xg_frame_restack (struct frame *f1, struct frame *f2, 
bool above_flag)
       XSETFRAME (frame2, f2);
 
       gdk_window_restack (gwin1, gwin2, above_flag);
+#ifndef HAVE_PGTK
       x_sync (f1);
+#else
+      gdk_flush();
+#endif
     }
   unblock_input ();
 }
@@ -1672,6 +1831,7 @@ xg_set_override_redirect (struct frame *f, Lisp_Object 
override_redirect)
   unblock_input ();
 }
 
+#ifndef HAVE_PGTK
 /* Set the frame icon to ICON_PIXMAP/MASK.  This must be done with GTK
    functions so GTK does not overwrite the icon.  */
 
@@ -1684,6 +1844,7 @@ xg_set_frame_icon (struct frame *f, Pixmap icon_pixmap, 
Pixmap icon_mask)
   if (gp)
     gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp);
 }
+#endif
 
 
 
@@ -2500,7 +2661,7 @@ xg_mark_data (void)
     {
       struct frame *f = XFRAME (frame);
 
-      if (FRAME_X_P (f) && FRAME_GTK_OUTER_WIDGET (f))
+      if ((FRAME_X_P (f) || FRAME_PGTK_P (f)) && FRAME_GTK_OUTER_WIDGET (f))
         {
           struct xg_frame_tb_info *tbinfo
             = g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
@@ -3529,7 +3690,7 @@ menubar_map_cb (GtkWidget *w, gpointer user_data)
 void
 xg_update_frame_menubar (struct frame *f)
 {
-  struct x_output *x = f->output_data.x;
+  xp_output *x = f->output_data.xp;
   GtkRequisition req;
 
   if (!x->menubar_widget || gtk_widget_get_mapped (x->menubar_widget))
@@ -3562,7 +3723,7 @@ xg_update_frame_menubar (struct frame *f)
 void
 free_frame_menubar (struct frame *f)
 {
-  struct x_output *x = f->output_data.x;
+  xp_output *x = f->output_data.xp;
 
   if (x->menubar_widget)
     {
@@ -3578,10 +3739,11 @@ free_frame_menubar (struct frame *f)
     }
 }
 
+#ifndef HAVE_PGTK
 bool
-xg_event_is_for_menubar (struct frame *f, const XEvent *event)
+xg_event_is_for_menubar (struct frame *f, const EVENT *event)
 {
-  struct x_output *x = f->output_data.x;
+  xp_output *x = f->output_data.xp;
   GList *iter;
   GdkRectangle rec;
   GList *list;
@@ -3628,6 +3790,7 @@ xg_event_is_for_menubar (struct frame *f, const XEvent 
*event)
   g_list_free (list);
   return iter != 0;
 }
+#endif
 
 
 
@@ -3783,6 +3946,7 @@ xg_get_default_scrollbar_height (struct frame *f)
   return scroll_bar_width_for_theme * xg_get_scale (f);
 }
 
+#ifndef HAVE_PGTK
 /* Return the scrollbar id for X Window WID on display DPY.
    Return -1 if WID not in id_to_widget.  */
 
@@ -3803,6 +3967,7 @@ xg_get_scroll_id_for_window (Display *dpy, Window wid)
 
   return -1;
 }
+#endif
 
 /* Callback invoked when scroll bar WIDGET is destroyed.
    DATA is the index into id_to_widget for WIDGET.
@@ -3852,7 +4017,7 @@ xg_finish_scroll_bar_creation (struct frame *f,
      also, which causes flicker.  Put an event box between the edit widget
      and the scroll bar, so the scroll bar instead draws itself on the
      event box window.  */
-  gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
+  gtk_fixed_put (GTK_FIXED (f->output_data.xp->edit_widget), webox, -1, -1);
   gtk_container_add (GTK_CONTAINER (webox), wscroll);
 
   xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f));
@@ -3862,7 +4027,12 @@ xg_finish_scroll_bar_creation (struct frame *f,
      real X window, it and its scroll-bar child try to draw on the
      Emacs main window, which we draw over using Xlib.  */
   gtk_widget_realize (webox);
+#ifdef HAVE_PGTK
+  gtk_widget_show_all(webox);
+#endif
+#ifndef HAVE_PGTK
   GTK_WIDGET_TO_X_WIN (webox);
+#endif
 
   /* Set the cursor to an arrow.  */
   xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
@@ -3967,7 +4137,7 @@ xg_update_scrollbar_pos (struct frame *f,
   GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
   if (wscroll)
     {
-      GtkWidget *wfixed = f->output_data.x->edit_widget;
+      GtkWidget *wfixed = f->output_data.xp->edit_widget;
       GtkWidget *wparent = gtk_widget_get_parent (wscroll);
       gint msl;
       int scale = xg_get_scale (f);
@@ -4007,7 +4177,11 @@ xg_update_scrollbar_pos (struct frame *f,
           /* Clear under old scroll bar position.  */
           oldw += (scale - 1) * oldw;
          oldx -= (scale - 1) * oldw;
+#ifndef HAVE_PGTK
           x_clear_area (f, oldx, oldy, oldw, oldh);
+#else
+          pgtk_clear_area (f, oldx, oldy, oldw, oldh);
+#endif
         }
 
       if (!hidden)
@@ -4015,15 +4189,23 @@ xg_update_scrollbar_pos (struct frame *f,
          GtkWidget *scrollbar = xg_get_widget_from_map (scrollbar_id);
          GtkWidget *webox = gtk_widget_get_parent (scrollbar);
 
+#ifndef HAVE_PGTK
          /* Don't obscure any child frames.  */
          XLowerWindow (FRAME_X_DISPLAY (f), GTK_WIDGET_TO_X_WIN (webox));
+#else
+         gdk_window_lower(gtk_widget_get_window(webox));
+#endif
        }
 
       /* GTK does not redraw until the main loop is entered again, but
          if there are no X events pending we will not enter it.  So we sync
          here to get some events.  */
 
+#ifndef HAVE_PGTK
       x_sync (f);
+#else
+      gdk_flush();
+#endif
       SET_FRAME_GARBAGED (f);
       cancel_mouse_face (f);
     }
@@ -4048,7 +4230,7 @@ xg_update_horizontal_scrollbar_pos (struct frame *f,
 
   if (wscroll)
     {
-      GtkWidget *wfixed = f->output_data.x->edit_widget;
+      GtkWidget *wfixed = f->output_data.xp->edit_widget;
       GtkWidget *wparent = gtk_widget_get_parent (wscroll);
       gint msl;
       int scale = xg_get_scale (f);
@@ -4084,7 +4266,11 @@ xg_update_horizontal_scrollbar_pos (struct frame *f,
         }
       if (oldx != -1 && oldw > 0 && oldh > 0)
         /* Clear under old scroll bar position.  */
+#ifndef HAVE_PGTK
         x_clear_area (f, oldx, oldy, oldw, oldh);
+#else
+        pgtk_clear_area (f, oldx, oldy, oldw, oldh);
+#endif
 
       /* GTK does not redraw until the main loop is entered again, but
          if there are no X events pending we will not enter it.  So we sync
@@ -4095,11 +4281,19 @@ xg_update_horizontal_scrollbar_pos (struct frame *f,
          xg_get_widget_from_map (scrollbar_id);
        GtkWidget *webox = gtk_widget_get_parent (scrollbar);
 
+#ifndef HAVE_PGTK
        /* Don't obscure any child frames.  */
        XLowerWindow (FRAME_X_DISPLAY (f), GTK_WIDGET_TO_X_WIN (webox));
+#else
+       gdk_window_lower(gtk_widget_get_window(webox));
+#endif
       }
 
+#ifndef HAVE_PGTK
       x_sync (f);
+#else
+      gdk_flush();
+#endif
       SET_FRAME_GARBAGED (f);
       cancel_mouse_face (f);
     }
@@ -4128,6 +4322,8 @@ xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
 
   struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
 
+  PGTK_TRACE("xg_set_toolkit_scroll_bar_thumb: 
----------------------------------");
+  PGTK_TRACE("xg_set_toolkit_scroll_bar_thumb: %p, %d, %d, %d.", bar, portion, 
position, whole);
   if (wscroll && bar->dragging == -1)
     {
       GtkAdjustment *adj;
@@ -4159,17 +4355,26 @@ xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
           top = (gdouble) position / whole;
           shown = (gdouble) portion / whole;
         }
+      PGTK_TRACE("xg_set_toolkit_scroll_bar_thumb: position=%d, portion=%d, 
whole=%d", position, portion, whole);
+      PGTK_TRACE("xg_set_toolkit_scroll_bar_thumb: top=%f, shown=%f", top, 
shown);
 
+      PGTK_TRACE("xg_set_toolkit_scroll_bar_thumb: shown*range=%f", shown * 
XG_SB_RANGE);
       size = clip_to_bounds (1, shown * XG_SB_RANGE, XG_SB_RANGE);
+      PGTK_TRACE("xg_set_toolkit_scroll_bar_thumb: size=%d.", size);
+      PGTK_TRACE("xg_set_toolkit_scroll_bar_thumb: top*range=%f.", top * 
XG_SB_RANGE);
+      PGTK_TRACE("xg_set_toolkit_scroll_bar_thumb: max-size=%d.", XG_SB_MAX - 
size);
       value = clip_to_bounds (XG_SB_MIN, top * XG_SB_RANGE, XG_SB_MAX - size);
+      PGTK_TRACE("xg_set_toolkit_scroll_bar_thumb: value=%d.", value);
 
       /* Assume all lines are of equal size.  */
       new_step = size / max (1, FRAME_LINES (f));
 
       old_size = gtk_adjustment_get_page_size (adj);
+      PGTK_TRACE("xg_set_toolkit_scroll_bar_thumb: old_size=%d, size=%d", 
old_size, size);
       if (old_size != size)
        {
          int old_step = gtk_adjustment_get_step_increment (adj);
+         PGTK_TRACE("xg_set_toolkit_scroll_bar_thumb: old_step=%d, 
new_step=%d", old_step, new_step);
          if (old_step != new_step)
            {
              gtk_adjustment_set_page_size (adj, size);
@@ -4180,6 +4385,8 @@ xg_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar,
            }
        }
 
+      PGTK_TRACE("xg_set_toolkit_scroll_bar_thumb: changed=%d, old=%d, 
value=%d.",
+                changed, int_gtk_range_get_value (GTK_RANGE (wscroll)), value);
       if (changed || int_gtk_range_get_value (GTK_RANGE (wscroll)) != value)
       {
         block_input ();
@@ -4243,14 +4450,24 @@ xg_set_toolkit_horizontal_scroll_bar_thumb (struct 
scroll_bar *bar,
    frame.  This function does additional checks.  */
 
 bool
-xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
+xg_event_is_for_scrollbar (struct frame *f, const EVENT *event)
 {
   bool retval = 0;
 
-  if (f && event->type == ButtonPress && event->xbutton.button < 4)
+  if (f
+#ifndef HAVE_PGTK
+      && event->type == ButtonPress && event->xbutton.button < 4
+#else
+      && event->type == GDK_BUTTON_PRESS && event->button.button < 4
+#endif
+      )
     {
       /* Check if press occurred outside the edit widget.  */
+#ifndef HAVE_PGTK
       GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
+#else
+      GdkDisplay *gdpy = FRAME_X_DISPLAY(f);
+#endif
       GdkWindow *gwin;
 #ifdef HAVE_GTK3
 #if GTK_CHECK_VERSION (3, 20, 0)
@@ -4264,11 +4481,17 @@ xg_event_is_for_scrollbar (struct frame *f, const 
XEvent *event)
 #else
       gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL);
 #endif
-      retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget);
+      retval = gwin != gtk_widget_get_window (f->output_data.xp->edit_widget);
     }
   else if (f
+#ifndef HAVE_PGTK
            && ((event->type == ButtonRelease && event->xbutton.button < 4)
-               || event->type == MotionNotify))
+               || event->type == MotionNotify)
+#else
+           && ((event->type == GDK_BUTTON_RELEASE && event->button.button < 4)
+               || event->type == GDK_MOTION_NOTIFY)
+#endif
+          )
     {
       /* If we are releasing or moving the scroll bar, it has the grab.  */
       GtkWidget *w = gtk_grab_get_current ();
@@ -4346,7 +4569,11 @@ draw_page (GtkPrintOperation *operation, GtkPrintContext 
*context,
   struct frame *f = XFRAME (Fnth (make_fixnum (page_nr), frames));
   cairo_t *cr = gtk_print_context_get_cairo_context (context);
 
+#ifndef HAVE_PGTK
   x_cr_draw_frame (cr, f);
+#else
+  pgtk_cr_draw_frame (cr, f);
+#endif
 }
 
 void
@@ -4447,7 +4674,11 @@ xg_tool_bar_callback (GtkWidget *w, gpointer client_data)
   /* Convert between the modifier bits GDK uses and the modifier bits
      Emacs uses.  This assumes GDK and X masks are the same, which they are 
when
      this is written.  */
+#ifndef HAVE_PGTK
   event.modifiers = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), mod);
+#else
+  event.modifiers = pgtk_gtk_to_emacs_modifiers (mod);
+#endif
   kbd_buffer_store_event (&event);
 
   /* Return focus to the frame after we have clicked on a detached
@@ -4517,7 +4748,6 @@ xg_tool_bar_help_callback (GtkWidget *w,
 
    Returns FALSE to tell GTK to keep processing this event.  */
 
-#ifndef HAVE_GTK3
 static gboolean
 xg_tool_bar_item_expose_callback (GtkWidget *w,
                                   GdkEventExpose *event,
@@ -4537,14 +4767,13 @@ xg_tool_bar_item_expose_callback (GtkWidget *w,
 
   return FALSE;
 }
-#endif
 
 /* Attach a tool bar to frame F.  */
 
 static void
 xg_pack_tool_bar (struct frame *f, Lisp_Object pos)
 {
-  struct x_output *x = f->output_data.x;
+  xp_output *x = f->output_data.xp;
   bool into_hbox = EQ (pos, Qleft) || EQ (pos, Qright);
   GtkWidget *top_widget = x->toolbar_widget;
 
@@ -4603,7 +4832,7 @@ tb_size_cb (GtkWidget    *widget,
 static void
 xg_create_tool_bar (struct frame *f)
 {
-  struct x_output *x = f->output_data.x;
+  xp_output *x = f->output_data.xp;
 #ifdef HAVE_GTK3
   GtkStyleContext *gsty;
 #endif
@@ -4842,7 +5071,7 @@ xg_tool_item_stale_p (GtkWidget *wbutton, const char 
*stock_name,
 static bool
 xg_update_tool_bar_sizes (struct frame *f)
 {
-  struct x_output *x = f->output_data.x;
+  xp_output *x = f->output_data.xp;
   GtkRequisition req;
   int nl = 0, nr = 0, nt = 0, nb = 0;
   GtkWidget *top_widget = x->toolbar_widget;
@@ -4928,7 +5157,11 @@ void
 update_frame_tool_bar (struct frame *f)
 {
   int i, j;
-  struct x_output *x = f->output_data.x;
+#ifndef HAVE_PGTK
+  struct x_output *x = f->output_data.xp;
+#else
+  struct pgtk_output *x = f->output_data.pgtk;
+#endif
   int hmargin = 0, vmargin = 0;
   GtkToolbar *wtoolbar;
   GtkToolItem *ti;
@@ -5245,7 +5478,7 @@ update_frame_tool_bar (struct frame *f)
 void
 free_frame_tool_bar (struct frame *f)
 {
-  struct x_output *x = f->output_data.x;
+  xp_output *x = f->output_data.xp;
 
   if (x->toolbar_widget)
     {
@@ -5291,7 +5524,7 @@ free_frame_tool_bar (struct frame *f)
 void
 xg_change_toolbar_position (struct frame *f, Lisp_Object pos)
 {
-  struct x_output *x = f->output_data.x;
+  xp_output *x = f->output_data.xp;
   GtkWidget *top_widget = x->toolbar_widget;
 
   if (! x->toolbar_widget || ! top_widget)
diff --git a/src/gtkutil.h b/src/gtkutil.h
index 5419167..5087b31 100644
--- a/src/gtkutil.h
+++ b/src/gtkutil.h
@@ -25,7 +25,13 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 #include <gtk/gtk.h>
 #include "../lwlib/lwlib-widget.h"
+#ifdef HAVE_PGTK
+#include "pgtkterm.h"
+#define EVENT GdkEvent
+#else
 #include "xterm.h"
+#define EVENT XEvent
+#endif
 
 /* Minimum and maximum values used for GTK scroll bars  */
 
@@ -105,7 +111,7 @@ extern void xg_modify_menubar_widgets (GtkWidget *menubar,
 
 extern void xg_update_frame_menubar (struct frame *f);
 
-extern bool xg_event_is_for_menubar (struct frame *, const XEvent *);
+extern bool xg_event_is_for_menubar (struct frame *, const EVENT *);
 
 extern ptrdiff_t xg_get_scroll_id_for_window (Display *dpy, Window wid);
 
@@ -142,7 +148,7 @@ extern void xg_set_toolkit_horizontal_scroll_bar_thumb 
(struct scroll_bar *bar,
                                                        int portion,
                                                        int position,
                                                        int whole);
-extern bool xg_event_is_for_scrollbar (struct frame *, const XEvent *);
+extern bool xg_event_is_for_scrollbar (struct frame *, const EVENT *);
 extern int xg_get_default_scrollbar_width (struct frame *f);
 extern int xg_get_default_scrollbar_height (struct frame *f);
 
@@ -157,9 +163,15 @@ extern void xg_frame_set_char_size (struct frame *f, int 
width, int height);
 extern GtkWidget * xg_win_to_widget (Display *dpy, Window wdesc);
 
 extern int xg_get_scale (struct frame *f);
+#ifndef HAVE_PGTK
 extern void xg_display_open (char *display_name, Display **dpy);
 extern void xg_display_close (Display *dpy);
 extern GdkCursor * xg_create_default_cursor (Display *dpy);
+#else
+extern void xg_display_open (char *display_name, GdkDisplay **dpy);
+extern void xg_display_close (GdkDisplay *gdpy);
+extern GdkCursor * xg_create_default_cursor (GdkDisplay *gdpy);
+#endif
 
 extern bool xg_create_frame_widgets (struct frame *f);
 extern void xg_free_frame_widgets (struct frame *f);
diff --git a/src/image.c b/src/image.c
index 5eb4132..74cd6b8 100644
--- a/src/image.c
+++ b/src/image.c
@@ -129,6 +129,10 @@ typedef struct ns_bitmap_record Bitmap_Record;
 
 #endif /* HAVE_NS */
 
+#ifdef HAVE_PGTK
+typedef struct pgtk_bitmap_record Bitmap_Record;
+#endif /* HAVE_PGTK */
+
 #if (defined HAVE_X_WINDOWS \
      && ! (defined HAVE_NTGUI || defined USE_CAIRO || defined HAVE_NS))
 /* W32_TODO : Color tables on W32.  */
@@ -430,6 +434,14 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
       return -1;
 #endif
 
+#ifdef HAVE_PGTK
+  Emacs_Pixmap bitmap = image_pix_container_create_from_bitmap_data(f, bits,
+                                                                   width,
+                                                                   height,
+                                                                   0xffffffff,
+                                                                   0xff000000);
+#endif
+
   id = image_allocate_bitmap_record (f);
 
 #ifdef HAVE_NS
@@ -437,6 +449,11 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
   dpyinfo->bitmaps[id - 1].depth = 1;
 #endif
 
+#ifdef HAVE_PGTK
+  dpyinfo->bitmaps[id - 1].img = bitmap;
+  dpyinfo->bitmaps[id - 1].depth = 1;
+#endif
+
   dpyinfo->bitmaps[id - 1].file = NULL;
   dpyinfo->bitmaps[id - 1].height = height;
   dpyinfo->bitmaps[id - 1].width = width;
@@ -489,6 +506,10 @@ image_create_bitmap_from_file (struct frame *f, 
Lisp_Object file)
   return id;
 #endif
 
+#ifdef HAVE_PGTK
+  return -1;   // fixme:
+#endif
+
 #ifdef HAVE_X_WINDOWS
   unsigned int width, height;
   Pixmap bitmap;
@@ -561,6 +582,9 @@ free_bitmap_record (Display_Info *dpyinfo, Bitmap_Record 
*bm)
   ns_release_object (bm->img);
 #endif
 
+#ifdef HAVE_PGTK
+#endif
+
   if (bm->file)
     {
       xfree (bm->file);
@@ -1320,7 +1344,6 @@ image_ascent (struct image *img, struct face *face, 
struct glyph_slice *slice)
   return ascent;
 }
 
-
 
 /* Image background colors.  */
 
@@ -1344,6 +1367,7 @@ four_corners_best (Emacs_Pix_Context pimg, int *corners,
       corner_pixels[3] = GET_PIXEL (pimg, corners[LEFT_CORNER], 
corners[BOT_CORNER] - 1);
     }
   else
+
     {
       /* Get the colors at the corner_pixels of pimg.  */
       corner_pixels[0] = GET_PIXEL (pimg, 0, 0);
@@ -3908,6 +3932,13 @@ xbm_load (struct frame *f, struct image *img)
                              XPM images
  ***********************************************************************/
 
+#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK)
+
+static bool xpm_image_p (Lisp_Object object);
+static bool xpm_load (struct frame *f, struct image *img);
+
+#endif /* HAVE_XPM || HAVE_NS */
+
 #ifdef HAVE_XPM
 #ifdef HAVE_NTGUI
 /* Indicate to xpm.h that we don't have Xlib.  */
@@ -4795,7 +4826,7 @@ xpm_load_image (struct frame *f,
   Lisp_Object (*get_color_table) (Lisp_Object, const char *, int);
   Lisp_Object frame, color_symbols, color_table;
   int best_key;
-#ifndef HAVE_NS
+#if !defined(HAVE_NS)
   bool have_mask = false;
 #endif
   Emacs_Pix_Container ximg = NULL, mask_img = NULL;
@@ -4849,7 +4880,7 @@ xpm_load_image (struct frame *f,
     }
 
   if (!image_create_x_image_and_pixmap (f, img, width, height, 0, &ximg, 0)
-#ifndef HAVE_NS
+#if !defined(HAVE_NS)
       || !image_create_x_image_and_pixmap (f, img, width, height, 1,
                                           &mask_img, 1)
 #endif
@@ -4977,7 +5008,7 @@ xpm_load_image (struct frame *f,
 
          PUT_PIXEL (ximg, x, y,
                     FIXNUMP (color_val) ? XFIXNUM (color_val) : frame_fg);
-#ifndef HAVE_NS
+#if !defined(HAVE_NS)
          PUT_PIXEL (mask_img, x, y,
                     (!EQ (color_val, Qt) ? PIX_MASK_DRAW
                      : (have_mask = true, PIX_MASK_RETAIN)));
@@ -4998,7 +5029,7 @@ xpm_load_image (struct frame *f,
     IMAGE_BACKGROUND (img, f, ximg);
 
   image_put_x_image (f, img, ximg, 0);
-#ifndef HAVE_NS
+#if !defined(HAVE_NS)
   if (have_mask)
     {
       /* Fill in the background_transparent field while we have the
@@ -5729,7 +5760,7 @@ image_disable_image (struct frame *f, struct image *img)
   if (n_planes < 2 || cross_disabled_images)
     {
 #ifndef HAVE_NTGUI
-#ifndef HAVE_NS  /* TODO: NS support, however this not needed for toolbars */
+#if !defined(HAVE_NS)  /* TODO: NS support, however this not needed for 
toolbars */
 
 #ifndef USE_CAIRO
 #define CrossForeground(f) BLACK_PIX_DEFAULT (f)
@@ -5807,7 +5838,7 @@ image_build_heuristic_mask (struct frame *f, struct image 
*img,
     image_clear_image_1 (f, img, CLEAR_IMAGE_MASK);
 
 #ifndef HAVE_NTGUI
-#ifndef HAVE_NS
+#if !defined HAVE_NS
   /* Create an image and pixmap serving as mask.  */
   if (! image_create_x_image_and_pixmap (f, img, img->width, img->height, 1,
                                         &mask_img, 1))
@@ -5869,7 +5900,7 @@ image_build_heuristic_mask (struct frame *f, struct image 
*img,
       if (XGetPixel (ximg, x, y) == bg)
         ns_set_alpha (ximg, x, y, 0);
 #endif /* HAVE_NS */
-#ifndef HAVE_NS
+#if !defined HAVE_NS
   /* Fill in the background_transparent field while we have the mask handy. */
   image_background_transparent (img, f, mask_img);
 
@@ -9314,8 +9345,8 @@ imagemagick_load_image (struct frame *f, struct image 
*img,
                                           color_scale * pixel.red,
                                           color_scale * pixel.green,
                                           color_scale * pixel.blue));
-            }
-        }
+           }
+       }
       DestroyPixelIterator (iterator);
     }
 
@@ -10511,7 +10542,7 @@ static struct image_type const image_types[] =
  { SYMBOL_INDEX (Qjpeg), jpeg_image_p, jpeg_load, image_clear_image,
    IMAGE_TYPE_INIT (init_jpeg_functions) },
 #endif
-#if defined HAVE_XPM || defined HAVE_NS
+#if defined HAVE_XPM || defined HAVE_NS || defined USE_CAIRO
  { SYMBOL_INDEX (Qxpm), xpm_image_p, xpm_load, image_clear_image,
    IMAGE_TYPE_INIT (init_xpm_functions) },
 #endif
diff --git a/src/lisp.h b/src/lisp.h
index 76d7420..718c0f1 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3298,7 +3298,7 @@ struct frame;
 #endif
 
 /* Define if the windowing system provides a tool-bar.  */
-#if defined (USE_GTK) || defined (HAVE_NS)
+#if (defined (USE_GTK) && !defined(HAVE_PGTK)) || defined (HAVE_NS)
 #define HAVE_EXT_TOOL_BAR true
 #endif
 
diff --git a/src/pgtkfns.c b/src/pgtkfns.c
new file mode 100644
index 0000000..39f5887
--- /dev/null
+++ b/src/pgtkfns.c
@@ -0,0 +1,2778 @@
+/* Functions for the pure Gtk+-3.
+
+Copyright (C) 1989, 1992-1994, 2005-2006, 2008-2017 Free Software
+Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* This should be the first include, as it may set up #defines affecting
+   interpretation of even the system includes. */
+#include <config.h>
+
+#include <math.h>
+#include <c-strcase.h>
+
+#include "lisp.h"
+#include "blockinput.h"
+#include "gtkutil.h"
+#include "window.h"
+#include "character.h"
+#include "buffer.h"
+#include "keyboard.h"
+#include "termhooks.h"
+#include "fontset.h"
+#include "font.h"
+#include "xsettings.h"
+
+
+#ifdef HAVE_PGTK
+
+//static EmacsTooltip *pgtk_tooltip = nil;
+
+/* Static variables to handle applescript execution.  */
+static Lisp_Object as_script, *as_result;
+static int as_status;
+
+static ptrdiff_t image_cache_refcount;
+
+static struct pgtk_display_info *pgtk_display_info_for_name (Lisp_Object);
+static void pgtk_set_name_as_filename (struct frame *);
+
+static const char *pgtk_app_name = "Emacs";
+
+/* ==========================================================================
+
+    Internal utility functions
+
+   ========================================================================== 
*/
+
+static struct pgtk_display_info *
+check_pgtk_display_info (Lisp_Object object)
+{
+  struct pgtk_display_info *dpyinfo = NULL;
+
+  if (NILP (object))
+    {
+      struct frame *sf = XFRAME (selected_frame);
+
+      if (FRAME_PGTK_P (sf) && FRAME_LIVE_P (sf))
+       dpyinfo = FRAME_DISPLAY_INFO (sf);
+      else if (x_display_list != 0)
+       dpyinfo = x_display_list;
+      else
+        error ("Frames are not in use or not initialized");
+    }
+  else if (TERMINALP (object))
+    {
+      struct terminal *t = decode_live_terminal (object);
+
+      if (t->type != output_pgtk)
+        error ("Terminal %d is not a display", t->id);
+
+      dpyinfo = t->display_info.pgtk;
+    }
+  else if (STRINGP (object))
+    dpyinfo = pgtk_display_info_for_name (object);
+  else
+    {
+      struct frame *f = decode_window_system_frame (object);
+      dpyinfo = FRAME_DISPLAY_INFO (f);
+    }
+
+  return dpyinfo;
+}
+
+/* Return the X display structure for the display named NAME.
+   Open a new connection if necessary.  */
+static struct pgtk_display_info *
+pgtk_display_info_for_name (Lisp_Object name)
+{
+  struct pgtk_display_info *dpyinfo;
+
+  CHECK_STRING (name);
+
+  for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
+    if (!NILP (Fstring_equal (XCAR (dpyinfo->name_list_element), name)))
+      return dpyinfo;
+
+  /* Use this general default value to start with.  */
+  Vx_resource_name = Vinvocation_name;
+
+  validate_x_resource_name ();
+
+  dpyinfo = pgtk_term_init (name, SSDATA (Vx_resource_name));
+
+  if (dpyinfo == 0)
+    error ("Cannot connect to display server %s", SDATA (name));
+
+  XSETFASTINT (Vwindow_system_version, 11);
+
+  return dpyinfo;
+}
+
+/* ==========================================================================
+
+    Frame parameter setters
+
+   ========================================================================== 
*/
+
+
+static void
+x_set_foreground_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  Emacs_Color col;
+
+  /* Must block_input, because pgtk_lisp_to_color does block/unblock_input
+     which means that col may be deallocated in its unblock_input if there
+     is user input, unless we also block_input.  */
+  block_input ();
+  if (pgtk_lisp_to_color (arg, &col))
+    {
+      store_frame_param (f, Qforeground_color, oldval);
+      unblock_input ();
+      error ("Unknown color");
+    }
+
+  FRAME_X_OUTPUT(f)->foreground_color = col.pixel;
+
+  FRAME_FOREGROUND_PIXEL (f) = col.pixel;
+
+  if (FRAME_GTK_WIDGET (f))
+    {
+      update_face_from_frame_parameter (f, Qforeground_color, arg);
+      /*recompute_basic_faces (f); */
+      if (FRAME_VISIBLE_P (f))
+        SET_FRAME_GARBAGED (f);
+    }
+  unblock_input ();
+}
+
+
+static void
+x_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  Emacs_Color col;
+
+  block_input ();
+  if (pgtk_lisp_to_color (arg, &col))
+    {
+      store_frame_param (f, Qbackground_color, oldval);
+      unblock_input ();
+      error ("Unknown color");
+    }
+
+  /* clear the frame */
+  if (FRAME_VISIBLE_P (f))
+    pgtk_clear_frame (f);
+
+  PGTK_TRACE("x_set_background_color: col.pixel=%08lx.", col.pixel);
+  FRAME_X_OUTPUT(f)->background_color = col.pixel;
+  FRAME_BACKGROUND_PIXEL (f) =
+    ARGB_TO_ULONG ((int)(0xff), (int)(col.red>>8), (int)(col.green>>8), 
(int)(col.blue>>8));
+
+  xg_set_background_color(f, col.pixel);
+  update_face_from_frame_parameter (f, Qbackground_color, arg);
+
+  PGTK_TRACE("visible_p=%d.", FRAME_VISIBLE_P(f));
+  if (FRAME_VISIBLE_P (f))
+    SET_FRAME_GARBAGED (f);
+
+  unblock_input ();
+}
+
+
+static void
+x_set_cursor_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  Emacs_Color col;
+
+  block_input ();
+  if (pgtk_lisp_to_color (arg, &col))
+    {
+      store_frame_param (f, Qcursor_color, oldval);
+      unblock_input ();
+      error ("Unknown color");
+    }
+
+  FRAME_CURSOR_COLOR (f) = col.pixel;
+
+  if (FRAME_VISIBLE_P (f))
+    {
+      x_update_cursor (f, 0);
+      x_update_cursor (f, 1);
+    }
+  update_face_from_frame_parameter (f, Qcursor_color, arg);
+  unblock_input ();
+}
+
+
+static void
+x_set_icon_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  GtkWidget *widget = FRAME_GTK_OUTER_WIDGET(f);
+  PGTK_TRACE ("x_set_icon_name");
+
+  /* see if it's changed */
+  if (STRINGP (arg))
+    {
+      if (STRINGP (oldval) && EQ (Fstring_equal (oldval, arg), Qt))
+        return;
+    }
+  else if (!STRINGP (oldval) && EQ (oldval, Qnil) == EQ (arg, Qnil))
+    return;
+
+  fset_icon_name (f, arg);
+
+  if (NILP (arg))
+    {
+      if (!NILP (f->title))
+        arg = f->title;
+      else
+        /* Explicit name and no icon-name -> explicit_name.  */
+        if (f->explicit_name)
+          arg = f->name;
+        else
+          {
+            /* No explicit name and no icon-name ->
+               name has to be rebuild from icon_title_format.  */
+            windows_or_buffers_changed = 67;
+            return;
+          }
+    }
+
+  gtk_window_set_icon_name(GTK_WINDOW(widget), SSDATA(arg));
+}
+
+static void
+pgtk_set_name_internal (struct frame *f, Lisp_Object name)
+{
+  Lisp_Object encoded_name, encoded_icon_name;
+  GtkWidget *widget = FRAME_GTK_OUTER_WIDGET (f);
+
+  encoded_name = ENCODE_UTF_8 (name);
+  gtk_window_set_title(GTK_WINDOW(widget), SSDATA (encoded_name));
+
+  if (!STRINGP (f->icon_name))
+    encoded_icon_name = encoded_name;
+  else
+    encoded_icon_name = ENCODE_UTF_8 (f->icon_name);
+  gtk_window_set_icon_name(GTK_WINDOW(widget), SSDATA (encoded_icon_name));
+}
+
+static void
+pgtk_set_name (struct frame *f, Lisp_Object name, int explicit)
+{
+  PGTK_TRACE ("pgtk_set_name");
+
+  /* Make sure that requests from lisp code override requests from
+     Emacs redisplay code.  */
+  if (explicit)
+    {
+      /* If we're switching from explicit to implicit, we had better
+         update the mode lines and thereby update the title.  */
+      if (f->explicit_name && NILP (name))
+        update_mode_lines = 12;
+
+      f->explicit_name = ! NILP (name);
+    }
+  else if (f->explicit_name)
+    return;
+
+  if (NILP (name))
+    name = build_string (pgtk_app_name);
+  else
+    CHECK_STRING (name);
+
+  /* Don't change the name if it's already NAME.  */
+  if (! NILP (Fstring_equal (name, f->name)))
+    return;
+
+  fset_name (f, name);
+
+  /* Title overrides explicit name.  */
+  if (! NILP (f->title))
+    name = f->title;
+
+  pgtk_set_name_internal (f, name);
+}
+
+
+/* This function should be called when the user's lisp code has
+   specified a name for the frame; the name will override any set by the
+   redisplay code.  */
+static void
+x_explicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  PGTK_TRACE ("x_explicitly_set_name");
+  pgtk_set_name (f, arg, 1);
+}
+
+
+/* This function should be called by Emacs redisplay code to set the
+   name; names set this way will never override names set by the user's
+   lisp code.  */
+void
+x_implicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  PGTK_TRACE ("x_implicitly_set_name");
+
+  Lisp_Object frame_title = buffer_local_value
+    (Qframe_title_format, XWINDOW (f->selected_window)->contents);
+  Lisp_Object icon_title = buffer_local_value
+    (Qicon_title_format, XWINDOW (f->selected_window)->contents);
+
+  if (FRAME_PGTK_P (f) && ((FRAME_ICONIFIED_P (f) && EQ (icon_title, Qt))
+                         || EQ (frame_title, Qt)))
+    pgtk_set_name_as_filename (f);
+  else
+    pgtk_set_name (f, arg, 0);
+}
+
+
+/* Change the title of frame F to NAME.
+   If NAME is nil, use the frame name as the title.  */
+
+static void
+x_set_title (struct frame *f, Lisp_Object name, Lisp_Object old_name)
+{
+  PGTK_TRACE ("x_set_title");
+  /* Don't change the title if it's already NAME.  */
+  if (EQ (name, f->title))
+    return;
+
+  update_mode_lines = 22;
+
+  fset_title (f, name);
+
+  if (NILP (name))
+    name = f->name;
+  else
+    CHECK_STRING (name);
+
+  pgtk_set_name_internal (f, name);
+}
+
+
+static void
+pgtk_set_name_as_filename (struct frame *f)
+{
+  GtkWidget *widget;
+  Lisp_Object name, filename;
+  Lisp_Object buf = XWINDOW (f->selected_window)->contents;
+  const char *title;
+  Lisp_Object encoded_name, encoded_filename;
+  const char *str;
+  PGTK_TRACE ("pgtk_set_name_as_filename");
+
+  if (f->explicit_name || ! NILP (f->title))
+    return;
+
+  block_input ();
+  filename = BVAR (XBUFFER (buf), filename);
+  name = BVAR (XBUFFER (buf), name);
+
+  if (NILP (name))
+    {
+      if (! NILP (filename))
+        name = Ffile_name_nondirectory (filename);
+      else
+        name = build_string (pgtk_app_name);
+    }
+
+  encoded_name = ENCODE_UTF_8 (name);
+
+  widget = FRAME_GTK_OUTER_WIDGET (f);
+
+  title = FRAME_ICONIFIED_P (f) ? gtk_window_get_icon_name(GTK_WINDOW(widget))
+                               : gtk_window_get_title(GTK_WINDOW(widget));
+
+  if (title && (! strcmp (title, SSDATA (encoded_name))))
+    {
+      unblock_input ();
+      return;
+    }
+
+  str = SSDATA (encoded_name);
+  if (str == NULL) str = "Bad coding";
+
+  if (FRAME_ICONIFIED_P (f))
+    gtk_window_set_icon_name(GTK_WINDOW(widget), str);
+  else
+    {
+      const char *fstr;
+
+      if (! NILP (filename))
+        {
+          encoded_filename = ENCODE_UTF_8 (filename);
+
+          fstr = SSDATA (encoded_filename);
+          if (fstr == NULL) fstr = "";
+        }
+      else
+        fstr = "";
+
+      gtk_window_set_title(GTK_WINDOW(widget), str);
+      fset_name (f, name);
+    }
+
+  unblock_input ();
+}
+
+
+void
+pgtk_set_doc_edited (void)
+{
+}
+
+
+static void
+x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
+{
+#if 0
+  int nlines;
+  if (FRAME_MINIBUF_ONLY_P (f))
+    return;
+
+  if (TYPE_RANGED_INTEGERP (int, value))
+    nlines = XFIXNUM (value);
+  else
+    nlines = 0;
+
+  FRAME_MENU_BAR_LINES (f) = 0;
+  if (nlines)
+    {
+      FRAME_EXTERNAL_MENU_BAR (f) = 1;
+      /* does for all frames, whereas we just want for one frame
+        [NSMenu setMenuBarVisible: YES]; */
+    }
+  else
+    {
+      if (FRAME_EXTERNAL_MENU_BAR (f) == 1)
+        free_frame_menubar (f);
+      /*      [NSMenu setMenuBarVisible: NO]; */
+      FRAME_EXTERNAL_MENU_BAR (f) = 0;
+    }
+#endif
+}
+
+
+/* toolbar support */
+static void
+x_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
+{
+#if 0
+  /* Currently, when the tool bar change state, the frame is resized.
+
+     TODO: It would be better if this didn't occur when 1) the frame
+     is full height or maximized or 2) when specified by
+     `frame-inhibit-implied-resize'. */
+  int nlines;
+
+  NSTRACE ("x_set_tool_bar_lines");
+
+  if (FRAME_MINIBUF_ONLY_P (f))
+    return;
+
+  if (RANGED_INTEGERP (0, value, INT_MAX))
+    nlines = XFIXNAT (value);
+  else
+    nlines = 0;
+
+  if (nlines)
+    {
+      FRAME_EXTERNAL_TOOL_BAR (f) = 1;
+      update_frame_tool_bar (f);
+    }
+  else
+    {
+      if (FRAME_EXTERNAL_TOOL_BAR (f))
+        {
+          free_frame_tool_bar (f);
+          FRAME_EXTERNAL_TOOL_BAR (f) = 0;
+
+          {
+            EmacsView *view = FRAME_PGTK_VIEW (f);
+            int fs_state = [view fullscreenState];
+
+            if (fs_state == FULLSCREEN_MAXIMIZED)
+              {
+                [view setFSValue:FULLSCREEN_WIDTH];
+              }
+            else if (fs_state == FULLSCREEN_HEIGHT)
+              {
+                [view setFSValue:FULLSCREEN_NONE];
+              }
+          }
+       }
+    }
+
+  {
+    int inhibit
+      = ((f->after_make_frame
+         && !f->tool_bar_resized
+         && (EQ (frame_inhibit_implied_resize, Qt)
+             || (CONSP (frame_inhibit_implied_resize)
+                 && !NILP (Fmemq (Qtool_bar_lines,
+                                  frame_inhibit_implied_resize))))
+         && NILP (get_frame_param (f, Qfullscreen)))
+        ? 0
+        : 2);
+
+    NSTRACE_MSG ("inhibit:%d", inhibit);
+
+    frame_size_history_add (f, Qupdate_frame_tool_bar, 0, 0, Qnil);
+    adjust_frame_size (f, -1, -1, inhibit, 0, Qtool_bar_lines);
+  }
+#endif
+}
+
+
+static void
+x_set_internal_border_width (struct frame *f, Lisp_Object arg, Lisp_Object 
oldval)
+{
+#if 0
+  int old_width = FRAME_INTERNAL_BORDER_WIDTH (f);
+
+  CHECK_TYPE_RANGED_INTEGER (int, arg);
+  f->internal_border_width = XFIXNUM (arg);
+  if (FRAME_INTERNAL_BORDER_WIDTH (f) < 0)
+    f->internal_border_width = 0;
+
+  if (FRAME_INTERNAL_BORDER_WIDTH (f) == old_width)
+    return;
+
+  if (FRAME_X_WINDOW (f) != 0)
+    adjust_frame_size (f, -1, -1, 3, 0, Qinternal_border_width);
+
+  SET_FRAME_GARBAGED (f);
+#endif
+}
+
+
+static void
+x_set_icon_type (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  /* This does not work if on Wayland, or if icon is defined in emacs.desktop
+   * even if on X11.
+   */
+  GdkPixbuf *pixbuf;
+  if (NILP (arg) || EQ (arg, Qt))
+    pixbuf = NULL;
+  else {
+    GError *err = NULL;
+    CHECK_STRING (arg);
+    pixbuf = gdk_pixbuf_new_from_file (SSDATA (arg), &err);
+    if (pixbuf == NULL) {
+      Lisp_Object msg = build_string (err->message);
+      g_error_free (err);
+      error ("%s", SSDATA (msg));
+    }
+  }
+
+  gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), pixbuf);
+  if (pixbuf != NULL)
+    g_object_unref (pixbuf);
+}
+
+/* This is the same as the xfns.c definition.  */
+static void
+x_set_cursor_type (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  set_frame_cursor_types (f, arg);
+}
+
+/* called to set mouse pointer color, but all other terms use it to
+   initialize pointer types (and don't set the color ;) */
+static void
+x_set_mouse_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+}
+
+
+static void
+x_icon (struct frame *f, Lisp_Object parms)
+/* --------------------------------------------------------------------------
+   Strangely-named function to set icon position parameters in frame.
+   This is irrelevant under macOS, but might be needed under GNUstep,
+   depending on the window manager used.  Note, this is not a standard
+   frame parameter-setter; it is called directly from x-create-frame.
+   -------------------------------------------------------------------------- 
*/
+{
+#if 0
+  Lisp_Object icon_x, icon_y;
+  struct pgtk_display_info *dpyinfo = check_pgtk_display_info (Qnil);
+
+  FRAME_X_OUTPUT(f)->icon_top = -1;
+  FRAME_X_OUTPUT(f)->icon_left = -1;
+
+  /* Set the position of the icon.  */
+  icon_x = x_get_arg (dpyinfo, parms, Qicon_left, 0, 0, RES_TYPE_NUMBER);
+  icon_y = x_get_arg (dpyinfo, parms, Qicon_top, 0, 0,  RES_TYPE_NUMBER);
+  if (!EQ (icon_x, Qunbound) && !EQ (icon_y, Qunbound))
+    {
+      CHECK_NUMBER (icon_x);
+      CHECK_NUMBER (icon_y);
+      FRAME_X_OUTPUT(f)->icon_top = XFIXNUM (icon_y);
+      FRAME_X_OUTPUT(f)->icon_left = XFIXNUM (icon_x);
+    }
+  else if (!EQ (icon_x, Qunbound) || !EQ (icon_y, Qunbound))
+    error ("Both left and top icon corners of icon must be specified");
+#endif
+}
+
+/**
+ * x_set_override_redirect:
+ *
+ * Set frame F's `override_redirect' parameter which, if non-nil, hints
+ * that the window manager doesn't want to deal with F.  Usually, such
+ * frames have no decorations and always appear on top of all frames.
+ *
+ * Some window managers may not honor this parameter.
+ */
+static void
+x_set_override_redirect (struct frame *f, Lisp_Object new_value, Lisp_Object 
old_value)
+{
+  if (!EQ (new_value, old_value))
+    {
+      /* Here (xfwm) override_redirect can be changed for invisible
+        frames only.  */
+      x_make_frame_invisible (f);
+
+      xg_set_override_redirect (f, new_value);
+
+      x_make_frame_visible (f);
+      FRAME_OVERRIDE_REDIRECT (f) = !NILP (new_value);
+    }
+}
+
+/* Note: see frame.c for template, also where generic functions are impl */
+frame_parm_handler pgtk_frame_parm_handlers[] =
+{
+  x_set_autoraise, /* generic OK */
+  x_set_autolower, /* generic OK */
+  x_set_background_color,
+  0, /* x_set_border_color,  may be impossible under Nextstep */
+  0, /* x_set_border_width,  may be impossible under Nextstep */
+  x_set_cursor_color,
+  x_set_cursor_type,
+  x_set_font, /* generic OK */
+  x_set_foreground_color,
+  x_set_icon_name,
+  x_set_icon_type,
+  x_set_internal_border_width, /* generic OK */
+  x_set_right_divider_width,
+  x_set_bottom_divider_width,
+  x_set_menu_bar_lines,
+  x_set_mouse_color,
+  x_explicitly_set_name,
+  x_set_scroll_bar_width, /* generic OK */
+  x_set_scroll_bar_height, /* generic OK */
+  x_set_title,
+  x_set_unsplittable, /* generic OK */
+  x_set_vertical_scroll_bars, /* generic OK */
+  x_set_horizontal_scroll_bars, /* generic OK */
+  x_set_visibility, /* generic OK */
+  x_set_tool_bar_lines,
+  0, /* x_set_scroll_bar_foreground, will ignore (not possible on NS) */
+  0, /* x_set_scroll_bar_background,  will ignore (not possible on NS) */
+  x_set_screen_gamma, /* generic OK */
+  x_set_line_spacing, /* generic OK, sets f->extra_line_spacing to int */
+  x_set_left_fringe, /* generic OK */
+  x_set_right_fringe, /* generic OK */
+  0, /* x_set_wait_for_wm, will ignore */
+  x_set_fullscreen, /* generic OK */
+  x_set_font_backend, /* generic OK */
+  x_set_alpha,
+  0, /* x_set_sticky */
+  0, /* x_set_tool_bar_position */
+  0, /* x_set_inhibit_double_buffering */
+  0, /*x_set_undecorated */
+  0, /* x_set_parent_frame, */
+  0, /* x_set_skip_taskbar */
+  x_set_no_focus_on_map,
+  x_set_no_accept_focus,
+  x_set_z_group,
+  x_set_override_redirect,
+  x_set_no_special_glyphs,
+};
+
+
+/* Handler for signals raised during x_create_frame and
+   x_create_tip_frame.  FRAME is the frame which is partially
+   constructed.  */
+
+static Lisp_Object
+unwind_create_frame (Lisp_Object frame)
+{
+  struct frame *f = XFRAME (frame);
+
+  /* If frame is already dead, nothing to do.  This can happen if the
+     display is disconnected after the frame has become official, but
+     before x_create_frame removes the unwind protect.  */
+  if (!FRAME_LIVE_P (f))
+    return Qnil;
+
+  /* If frame is ``official'', nothing to do.  */
+  if (NILP (Fmemq (frame, Vframe_list)))
+    {
+      /* If the frame's image cache refcount is still the same as our
+        private shadow variable, it means we are unwinding a frame
+        for which we didn't yet call init_frame_faces, where the
+        refcount is incremented.  Therefore, we increment it here, so
+        that free_frame_faces, called in x_free_frame_resources
+        below, will not mistakenly decrement the counter that was not
+        incremented yet to account for this new frame.  */
+      if (FRAME_IMAGE_CACHE (f) != NULL
+         && FRAME_IMAGE_CACHE (f)->refcount == image_cache_refcount)
+       FRAME_IMAGE_CACHE (f)->refcount++;
+
+      x_free_frame_resources (f);
+      free_glyphs (f);
+      return Qt;
+    }
+
+  return Qnil;
+}
+
+static void
+do_unwind_create_frame (Lisp_Object frame)
+{
+  unwind_create_frame (frame);
+}
+
+/* Return the pixel color value for color COLOR_NAME on frame F.  If F
+   is a monochrome frame, return MONO_COLOR regardless of what ARG says.
+   Signal an error if color can't be allocated.  */
+
+static int
+x_decode_color (struct frame *f, Lisp_Object color_name, int mono_color)
+{
+  Emacs_Color cdef;
+
+  CHECK_STRING (color_name);
+
+  /* Return MONO_COLOR for monochrome frames.  */
+  if (FRAME_DISPLAY_INFO (f)->n_planes == 1)
+    return mono_color;
+
+  /* x_defined_color is responsible for coping with failures
+     by looking for a near-miss.  */
+  if (pgtk_defined_color (f, SSDATA (color_name), &cdef, true, 0))
+    return cdef.pixel;
+
+  signal_error ("Undefined color", color_name);
+}
+
+static void
+x_default_font_parameter (struct frame *f, Lisp_Object parms)
+{
+  struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+  Lisp_Object font_param = x_get_arg (dpyinfo, parms, Qfont, NULL, NULL,
+                                      RES_TYPE_STRING);
+  Lisp_Object font = Qnil;
+  if (EQ (font_param, Qunbound))
+    font_param = Qnil;
+
+  if (NILP (font_param))
+    {
+      /* System font should take precedence over X resources.  We suggest this
+         regardless of font-use-system-font because .emacs may not have been
+         read yet.  */
+      const char *system_font = xsettings_get_system_font ();
+      if (system_font)
+       font = font_open_by_name (f, build_unibyte_string (system_font));
+    }
+
+  if (NILP (font))
+      font = !NILP (font_param) ? font_param
+      : x_get_arg (dpyinfo, parms, Qfont, "font", "Font", RES_TYPE_STRING);
+
+  if (! FONTP (font) && ! STRINGP (font))
+    {
+      const char *names[]
+       = {
+           "monospace-10",
+           "-adobe-courier-medium-r-*-*-*-120-*-*-*-*-iso8859-1",
+           "-misc-fixed-medium-r-normal-*-*-140-*-*-c-*-iso8859-1",
+           "-*-*-medium-r-normal-*-*-140-*-*-c-*-iso8859-1",
+           /* This was formerly the first thing tried, but it finds
+              too many fonts and takes too long.  */
+           "-*-*-medium-r-*-*-*-*-*-*-c-*-iso8859-1",
+           /* If those didn't work, look for something which will
+              at least work.  */
+           "-*-fixed-*-*-*-*-*-140-*-*-c-*-iso8859-1",
+           "fixed",
+           NULL };
+      int i;
+
+      for (i = 0; names[i]; i++)
+       {
+         font = font_open_by_name (f, build_unibyte_string (names[i]));
+         if (! NILP (font))
+           break;
+       }
+      if (NILP (font))
+       error ("No suitable font was found");
+    }
+  else if (!NILP (font_param))
+    {
+      /* Remember the explicit font parameter, so we can re-apply it after
+        we've applied the `default' face settings.  */
+      AUTO_FRAME_ARG (arg, Qfont_parameter, font_param);
+      x_set_frame_parameters (f, arg);
+    }
+
+  /* This call will make X resources override any system font setting.  */
+  x_default_parameter (f, parms, Qfont, font, "font", "Font", RES_TYPE_STRING);
+}
+
+/* ==========================================================================
+
+    Lisp definitions
+
+   ========================================================================== 
*/
+
+DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
+       1, 1, 0,
+       doc: /* Make a new X window, which is called a "frame" in Emacs terms.
+Return an Emacs frame object.  PARMS is an alist of frame parameters.
+If the parameters specify that the frame should not have a minibuffer,
+and do not specify a specific minibuffer window to use, then
+`default-minibuffer-frame' must be a frame whose minibuffer can be
+shared by the new frame.
+
+This function is an internal primitive--use `make-frame' instead.  */)
+  (Lisp_Object parms)
+{
+  struct frame *f;
+  Lisp_Object frame, tem;
+  Lisp_Object name;
+  bool minibuffer_only = false;
+  bool undecorated = false, override_redirect = false;
+  long window_prompting = 0;
+  ptrdiff_t count = SPECPDL_INDEX ();
+  Lisp_Object display;
+  struct pgtk_display_info *dpyinfo = NULL;
+  Lisp_Object parent, parent_frame;
+  struct kboard *kb;
+  int x_width = 0, x_height = 0;
+
+  parms = Fcopy_alist (parms);
+
+  /* Use this general default value to start with
+     until we know if this frame has a specified name.  */
+  Vx_resource_name = Vinvocation_name;
+
+  display = x_get_arg (dpyinfo, parms, Qterminal, 0, 0, RES_TYPE_NUMBER);
+  if (EQ (display, Qunbound))
+    display = x_get_arg (dpyinfo, parms, Qdisplay, 0, 0, RES_TYPE_STRING);
+  if (EQ (display, Qunbound))
+    display = Qnil;
+  dpyinfo = check_pgtk_display_info (display);
+  kb = dpyinfo->terminal->kboard;
+
+  if (!dpyinfo->terminal->name)
+    error ("Terminal is not live, can't create new frames on it");
+
+  name = x_get_arg (dpyinfo, parms, Qname, "name", "Name", RES_TYPE_STRING);
+  if (!STRINGP (name)
+      && ! EQ (name, Qunbound)
+      && ! NILP (name))
+    error ("Invalid frame name--not a string or nil");
+
+  if (STRINGP (name))
+    Vx_resource_name = name;
+
+  /* See if parent window is specified.  */
+  parent = x_get_arg (dpyinfo, parms, Qparent_id, NULL, NULL, RES_TYPE_NUMBER);
+  if (EQ (parent, Qunbound))
+    parent = Qnil;
+  if (! NILP (parent))
+    CHECK_NUMBER (parent);
+
+  frame = Qnil;
+  tem = x_get_arg (dpyinfo, parms, Qminibuffer, "minibuffer", "Minibuffer",
+                  RES_TYPE_SYMBOL);
+  if (EQ (tem, Qnone) || NILP (tem))
+    f = make_frame_without_minibuffer (Qnil, kb, display);
+  else if (EQ (tem, Qonly))
+    {
+      f = make_minibuffer_frame ();
+      minibuffer_only = true;
+    }
+  else if (WINDOWP (tem))
+    f = make_frame_without_minibuffer (tem, kb, display);
+  else
+    f = make_frame (true);
+
+  parent_frame = x_get_arg (dpyinfo, parms, Qparent_frame, NULL, NULL,
+                           RES_TYPE_SYMBOL);
+  /* Accept parent-frame iff parent-id was not specified.  */
+  if (!NILP (parent)
+      || EQ (parent_frame, Qunbound)
+      || NILP (parent_frame)
+      || !FRAMEP (parent_frame)
+      || !FRAME_LIVE_P (XFRAME (parent_frame))
+      || !FRAME_PGTK_P (XFRAME (parent_frame)))
+    parent_frame = Qnil;
+
+  fset_parent_frame (f, parent_frame);
+  store_frame_param (f, Qparent_frame, parent_frame);
+
+  if (!NILP (tem = (x_get_arg (dpyinfo, parms, Qundecorated, NULL, NULL,
+                              RES_TYPE_BOOLEAN)))
+      && !(EQ (tem, Qunbound)))
+    undecorated = true;
+
+  FRAME_UNDECORATED (f) = undecorated;
+  store_frame_param (f, Qundecorated, undecorated ? Qt : Qnil);
+
+  if (!NILP (tem = (x_get_arg (dpyinfo, parms, Qoverride_redirect, NULL, NULL,
+                              RES_TYPE_BOOLEAN)))
+      && !(EQ (tem, Qunbound)))
+    override_redirect = true;
+
+  FRAME_OVERRIDE_REDIRECT (f) = override_redirect;
+  store_frame_param (f, Qoverride_redirect, override_redirect ? Qt : Qnil);
+
+  XSETFRAME (frame, f);
+
+  f->terminal = dpyinfo->terminal;
+
+  f->output_method = output_pgtk;
+  FRAME_X_OUTPUT(f) = xzalloc (sizeof *FRAME_X_OUTPUT(f));
+#if 0
+  FRAME_X_OUTPUT(f)->icon_bitmap = -1;
+#endif
+  FRAME_FONTSET (f) = -1;
+#if 0
+  FRAME_X_OUTPUT(f)->scroll_bar_foreground_pixel = -1;
+  FRAME_X_OUTPUT(f)->scroll_bar_background_pixel = -1;
+#endif
+  FRAME_X_OUTPUT(f)->white_relief.pixel = -1;
+  FRAME_X_OUTPUT(f)->black_relief.pixel = -1;
+
+  fset_icon_name (f,
+                 x_get_arg (dpyinfo, parms, Qicon_name, "iconName", "Title",
+                            RES_TYPE_STRING));
+  if (! STRINGP (f->icon_name))
+    fset_icon_name (f, Qnil);
+
+  FRAME_DISPLAY_INFO (f) = dpyinfo;
+
+  /* With FRAME_DISPLAY_INFO set up, this unwind-protect is safe.  */
+  record_unwind_protect (do_unwind_create_frame, frame);
+
+  /* These colors will be set anyway later, but it's important
+     to get the color reference counts right, so initialize them!  */
+  {
+    Lisp_Object black;
+
+    /* Function x_decode_color can signal an error.  Make
+       sure to initialize color slots so that we won't try
+       to free colors we haven't allocated.  */
+    FRAME_FOREGROUND_PIXEL (f) = -1;
+    FRAME_BACKGROUND_PIXEL (f) = -1;
+    FRAME_X_OUTPUT(f)->cursor_color = -1;
+#if 0
+    FRAME_X_OUTPUT(f)->cursor_foreground_pixel = -1;
+    FRAME_X_OUTPUT(f)->border_pixel = -1;
+    FRAME_X_OUTPUT(f)->mouse_pixel = -1;
+#endif
+
+    black = build_string ("black");
+    FRAME_FOREGROUND_PIXEL (f)
+      = x_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+    FRAME_BACKGROUND_PIXEL (f)
+      = x_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+    FRAME_X_OUTPUT(f)->cursor_color
+      = x_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+#if 0
+    FRAME_X_OUTPUT(f)->cursor_foreground_pixel
+      = x_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+    FRAME_X_OUTPUT(f)->border_pixel
+      = x_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+    FRAME_X_OUTPUT(f)->mouse_pixel
+      = x_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+#endif
+  }
+
+  /* Specify the parent under which to make this X window.  */
+  if (!NILP (parent))
+    {
+      FRAME_X_OUTPUT(f)->parent_desc = (Window) XFIXNAT (parent);
+      FRAME_X_OUTPUT(f)->explicit_parent = true;
+    }
+  else
+    {
+      FRAME_X_OUTPUT(f)->parent_desc = FRAME_DISPLAY_INFO (f)->root_window;
+      FRAME_X_OUTPUT(f)->explicit_parent = false;
+    }
+
+  /* Set the name; the functions to which we pass f expect the name to
+     be set.  */
+  if (EQ (name, Qunbound) || NILP (name))
+    {
+      fset_name (f, build_string (dpyinfo->x_id_name));
+      f->explicit_name = false;
+    }
+  else
+    {
+      fset_name (f, name);
+      f->explicit_name = true;
+      /* Use the frame's title when getting resources for this frame.  */
+      specbind (Qx_resource_name, name);
+    }
+
+  register_font_driver (&ftcrfont_driver, f);
+
+  image_cache_refcount =
+    FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0;
+#if 0
+#ifdef GLYPH_DEBUG
+  dpyinfo_refcount = dpyinfo->reference_count;
+#endif /* GLYPH_DEBUG */
+#endif
+
+  x_default_parameter (f, parms, Qfont_backend, Qnil,
+                      "fontBackend", "FontBackend", RES_TYPE_STRING);
+
+  /* Extract the window parameters from the supplied values
+     that are needed to determine window geometry.  */
+  x_default_font_parameter (f, parms);
+  if (!FRAME_FONT (f))
+    {
+      delete_frame (frame, Qnoelisp);
+      error ("Invalid frame font");
+    }
+
+  /* Frame contents get displaced if an embedded X window has a border.  */
+#if 0
+  if (! FRAME_X_EMBEDDED_P (f))
+#endif
+    x_default_parameter (f, parms, Qborder_width, make_fixnum (0),
+                        "borderWidth", "BorderWidth", RES_TYPE_NUMBER);
+
+  /* This defaults to 1 in order to match xterm.  We recognize either
+     internalBorderWidth or internalBorder (which is what xterm calls
+     it).  */
+  if (NILP (Fassq (Qinternal_border_width, parms)))
+    {
+      Lisp_Object value;
+
+      value = x_get_arg (dpyinfo, parms, Qinternal_border_width,
+                        "internalBorder", "internalBorder", RES_TYPE_NUMBER);
+      if (! EQ (value, Qunbound))
+       parms = Fcons (Fcons (Qinternal_border_width, value),
+                      parms);
+    }
+  x_default_parameter (f, parms, Qinternal_border_width,
+                      make_fixnum (0),
+                      "internalBorderWidth", "internalBorderWidth",
+                      RES_TYPE_NUMBER);
+  x_default_parameter (f, parms, Qright_divider_width, make_fixnum (0),
+                      NULL, NULL, RES_TYPE_NUMBER);
+  x_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
+                      NULL, NULL, RES_TYPE_NUMBER);
+  x_default_parameter (f, parms, Qvertical_scroll_bars,
+                      Qright,
+                      "verticalScrollBars", "ScrollBars",
+                      RES_TYPE_SYMBOL);
+  x_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil,
+                      "horizontalScrollBars", "ScrollBars",
+                      RES_TYPE_SYMBOL);
+  /* Also do the stuff which must be set before the window exists.  */
+  x_default_parameter (f, parms, Qforeground_color, build_string ("black"),
+                      "foreground", "Foreground", RES_TYPE_STRING);
+  x_default_parameter (f, parms, Qbackground_color, build_string ("white"),
+                      "background", "Background", RES_TYPE_STRING);
+  x_default_parameter (f, parms, Qmouse_color, build_string ("black"),
+                      "pointerColor", "Foreground", RES_TYPE_STRING);
+  x_default_parameter (f, parms, Qborder_color, build_string ("black"),
+                      "borderColor", "BorderColor", RES_TYPE_STRING);
+  x_default_parameter (f, parms, Qscreen_gamma, Qnil,
+                      "screenGamma", "ScreenGamma", RES_TYPE_FLOAT);
+  x_default_parameter (f, parms, Qline_spacing, Qnil,
+                      "lineSpacing", "LineSpacing", RES_TYPE_NUMBER);
+  x_default_parameter (f, parms, Qleft_fringe, Qnil,
+                      "leftFringe", "LeftFringe", RES_TYPE_NUMBER);
+  x_default_parameter (f, parms, Qright_fringe, Qnil,
+                      "rightFringe", "RightFringe", RES_TYPE_NUMBER);
+  x_default_parameter (f, parms, Qno_special_glyphs, Qnil,
+                      NULL, NULL, RES_TYPE_BOOLEAN);
+
+#if 0
+  x_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_foreground,
+                                       "scrollBarForeground",
+                                       "ScrollBarForeground", true);
+  x_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_background,
+                                       "scrollBarBackground",
+                                       "ScrollBarBackground", false);
+#endif
+
+  /* Init faces before x_default_parameter is called for the
+     scroll-bar-width parameter because otherwise we end up in
+     init_iterator with a null face cache, which should not happen.  */
+  init_frame_faces (f);
+
+  /* We have to call adjust_frame_size here since otherwise
+     x_set_tool_bar_lines will already work with the character sizes
+     installed by init_frame_faces while the frame's pixel size is still
+     calculated from a character size of 1 and we subsequently hit the
+     (height >= 0) assertion in window_box_height.
+
+     The non-pixelwise code apparently worked around this because it
+     had one frame line vs one toolbar line which left us with a zero
+     root window height which was obviously wrong as well ...
+
+     Also process `min-width' and `min-height' parameters right here
+     because `frame-windows-min-size' needs them.  */
+  tem = x_get_arg (dpyinfo, parms, Qmin_width, NULL, NULL, RES_TYPE_NUMBER);
+  if (NUMBERP (tem))
+    store_frame_param (f, Qmin_width, tem);
+  tem = x_get_arg (dpyinfo, parms, Qmin_height, NULL, NULL, RES_TYPE_NUMBER);
+  if (NUMBERP (tem))
+    store_frame_param (f, Qmin_height, tem);
+  adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
+                    FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 5, true,
+                    Qx_create_frame_1);
+
+  /* Set the menu-bar-lines and tool-bar-lines parameters.  We don't
+     look up the X resources controlling the menu-bar and tool-bar
+     here; they are processed specially at startup, and reflected in
+     the values of the mode variables.  */
+
+  x_default_parameter (f, parms, Qmenu_bar_lines,
+                      NILP (Vmenu_bar_mode)
+                      ? make_fixnum (0) : make_fixnum (1),
+                      NULL, NULL, RES_TYPE_NUMBER);
+  x_default_parameter (f, parms, Qtool_bar_lines,
+                      NILP (Vtool_bar_mode)
+                      ? make_fixnum (0) : make_fixnum (1),
+                      NULL, NULL, RES_TYPE_NUMBER);
+
+  x_default_parameter (f, parms, Qbuffer_predicate, Qnil,
+                      "bufferPredicate", "BufferPredicate",
+                      RES_TYPE_SYMBOL);
+  x_default_parameter (f, parms, Qtitle, Qnil,
+                      "title", "Title", RES_TYPE_STRING);
+  x_default_parameter (f, parms, Qwait_for_wm, Qt,
+                      "waitForWM", "WaitForWM", RES_TYPE_BOOLEAN);
+  x_default_parameter (f, parms, Qtool_bar_position,
+                       FRAME_TOOL_BAR_POSITION (f), 0, 0, RES_TYPE_SYMBOL);
+  x_default_parameter (f, parms, Qinhibit_double_buffering, Qnil,
+                       "inhibitDoubleBuffering", "InhibitDoubleBuffering",
+                       RES_TYPE_BOOLEAN);
+
+  /* Compute the size of the X window.  */
+  window_prompting = x_figure_window_size (f, parms, true, &x_width, 
&x_height);
+
+  tem = x_get_arg (dpyinfo, parms, Qunsplittable, 0, 0, RES_TYPE_BOOLEAN);
+  f->no_split = minibuffer_only || EQ (tem, Qt);
+
+#if 0
+  x_icon_verify (f, parms);
+#endif
+
+  /* Create the X widget or window.  */
+  // x_window (f);
+  xg_create_frame_widgets (f);
+  pgtk_set_event_handler(f);
+
+
+#define INSTALL_CURSOR(FIELD, NAME) \
+  FRAME_X_OUTPUT(f)->FIELD = gdk_cursor_new_for_display(FRAME_X_DISPLAY(f), 
GDK_ ## NAME)
+
+  INSTALL_CURSOR(text_cursor, XTERM);
+  INSTALL_CURSOR(nontext_cursor, LEFT_PTR);
+  INSTALL_CURSOR(modeline_cursor, XTERM);
+  INSTALL_CURSOR(hand_cursor, HAND2);
+  INSTALL_CURSOR(hourglass_cursor, WATCH);
+  INSTALL_CURSOR(horizontal_drag_cursor, SB_H_DOUBLE_ARROW);
+  INSTALL_CURSOR(vertical_drag_cursor, SB_V_DOUBLE_ARROW);
+  INSTALL_CURSOR(left_edge_cursor, LEFT_SIDE);
+  INSTALL_CURSOR(right_edge_cursor, RIGHT_SIDE);
+  INSTALL_CURSOR(top_edge_cursor, TOP_SIDE);
+  INSTALL_CURSOR(bottom_edge_cursor, BOTTOM_SIDE);
+  INSTALL_CURSOR(top_left_corner_cursor, TOP_LEFT_CORNER);
+  INSTALL_CURSOR(top_right_corner_cursor, TOP_RIGHT_CORNER);
+  INSTALL_CURSOR(bottom_right_corner_cursor, BOTTOM_RIGHT_CORNER);
+  INSTALL_CURSOR(bottom_left_corner_cursor, BOTTOM_LEFT_CORNER);
+
+#undef INSTALL_CURSOR
+
+  x_icon (f, parms);
+#if 0
+  x_make_gc (f);
+#endif
+
+  /* Now consider the frame official.  */
+  f->terminal->reference_count++;
+  FRAME_DISPLAY_INFO (f)->reference_count++;
+  Vframe_list = Fcons (frame, Vframe_list);
+
+  /* We need to do this after creating the X window, so that the
+     icon-creation functions can say whose icon they're describing.  */
+  x_default_parameter (f, parms, Qicon_type, Qt,
+                      "bitmapIcon", "BitmapIcon", RES_TYPE_BOOLEAN);
+
+  x_default_parameter (f, parms, Qauto_raise, Qnil,
+                      "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+  x_default_parameter (f, parms, Qauto_lower, Qnil,
+                      "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+  x_default_parameter (f, parms, Qcursor_type, Qbox,
+                      "cursorType", "CursorType", RES_TYPE_SYMBOL);
+  x_default_parameter (f, parms, Qscroll_bar_width, Qnil,
+                      "scrollBarWidth", "ScrollBarWidth",
+                      RES_TYPE_NUMBER);
+  x_default_parameter (f, parms, Qscroll_bar_height, Qnil,
+                      "scrollBarHeight", "ScrollBarHeight",
+                      RES_TYPE_NUMBER);
+  x_default_parameter (f, parms, Qalpha, Qnil,
+                      "alpha", "Alpha", RES_TYPE_NUMBER);
+
+#if 0
+  if (!NILP (parent_frame))
+    {
+      struct frame *p = XFRAME (parent_frame);
+
+      block_input ();
+      XReparentWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
+                      FRAME_X_WINDOW (p), f->left_pos, f->top_pos);
+      unblock_input ();
+    }
+#endif
+
+  x_default_parameter (f, parms, Qno_focus_on_map, Qnil,
+                      NULL, NULL, RES_TYPE_BOOLEAN);
+  x_default_parameter (f, parms, Qno_accept_focus, Qnil,
+                      NULL, NULL, RES_TYPE_BOOLEAN);
+
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
+  /* Create the menu bar.  */
+  if (!minibuffer_only && FRAME_EXTERNAL_MENU_BAR (f))
+    {
+#if 0
+      /* If this signals an error, we haven't set size hints for the
+        frame and we didn't make it visible.  */
+      initialize_frame_menubar (f);
+#endif
+
+#ifndef USE_GTK
+      /* This is a no-op, except under Motif where it arranges the
+        main window for the widgets on it.  */
+      lw_set_main_areas (FRAME_X_OUTPUT(f)->column_widget,
+                        FRAME_X_OUTPUT(f)->menubar_widget,
+                        FRAME_X_OUTPUT(f)->edit_widget);
+#endif /* not USE_GTK */
+    }
+#endif /* USE_X_TOOLKIT || USE_GTK */
+
+  /* Consider frame official, now.  */
+  f->can_x_set_window_size = true;
+
+  if (x_width > 0)
+    SET_FRAME_WIDTH (f, x_width);
+  if (x_height > 0)
+    SET_FRAME_HEIGHT (f, x_height);
+
+  /* Tell the server what size and position, etc, we want, and how
+     badly we want them.  This should be done after we have the menu
+     bar so that its size can be taken into account.  */
+  block_input ();
+  x_wm_set_size_hint (f, window_prompting, false);
+  unblock_input ();
+
+  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                    0, true, Qx_create_frame_2);
+
+  /* Process fullscreen parameter here in the hope that normalizing a
+     fullheight/fullwidth frame will produce the size set by the last
+     adjust_frame_size call.  */
+  x_default_parameter (f, parms, Qfullscreen, Qnil,
+                      "fullscreen", "Fullscreen", RES_TYPE_SYMBOL);
+
+  /* Make the window appear on the frame and enable display, unless
+     the caller says not to.  However, with explicit parent, Emacs
+     cannot control visibility, so don't try.  */
+  if (!FRAME_X_OUTPUT(f)->explicit_parent)
+    {
+      Lisp_Object visibility
+       = x_get_arg (dpyinfo, parms, Qvisibility, 0, 0, RES_TYPE_SYMBOL);
+
+      if (EQ (visibility, Qicon))
+       x_iconify_frame (f);
+      else
+       {
+         if (EQ (visibility, Qunbound))
+           visibility = Qt;
+
+         if (!NILP (visibility))
+           x_make_frame_visible (f);
+       }
+
+      store_frame_param (f, Qvisibility, visibility);
+    }
+
+  /* Works iff frame has been already mapped.  */
+  x_default_parameter (f, parms, Qskip_taskbar, Qnil,
+                      NULL, NULL, RES_TYPE_BOOLEAN);
+  /* The `z-group' parameter works only for visible frames.  */
+  x_default_parameter (f, parms, Qz_group, Qnil,
+                      NULL, NULL, RES_TYPE_SYMBOL);
+
+  /* Initialize `default-minibuffer-frame' in case this is the first
+     frame on this terminal.  */
+  if (FRAME_HAS_MINIBUF_P (f)
+      && (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
+          || !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame)))))
+    kset_default_minibuffer_frame (kb, frame);
+
+  /* All remaining specified parameters, which have not been "used"
+     by x_get_arg and friends, now go in the misc. alist of the frame.  */
+  for (tem = parms; CONSP (tem); tem = XCDR (tem))
+    if (CONSP (XCAR (tem)) && !NILP (XCAR (XCAR (tem))))
+      fset_param_alist (f, Fcons (XCAR (tem), f->param_alist));
+
+  FRAME_X_OUTPUT(f)->cr_surface_visible_bell = NULL;
+  FRAME_X_OUTPUT(f)->atimer_visible_bell = NULL;
+
+  /* Make sure windows on this frame appear in calls to next-window
+     and similar functions.  */
+  Vwindow_list = Qnil;
+
+ return unbind_to (count, frame);
+}
+
+void
+x_focus_frame (struct frame *f, bool noactivate)
+{
+  struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+
+#if 0
+  if (dpyinfo->x_focus_frame != f)
+    {
+      EmacsView *view = FRAME_PGTK_VIEW (f);
+      block_input ();
+      [NSApp activateIgnoringOtherApps: YES];
+      [[view window] makeKeyAndOrderFront: view];
+      unblock_input ();
+    }
+#endif
+}
+
+#if 0
+static int
+pgtk_window_is_ancestor (PGTKWindow *win, PGTKWindow *candidate)
+/* Test whether CANDIDATE is an ancestor window of WIN. */
+{
+  if (candidate == NULL)
+    return 0;
+  else if (win == candidate)
+    return 1;
+  else
+    return pgtk_window_is_ancestor(win, [candidate parentWindow]);
+}
+#endif
+
+DEFUN ("pgtk-frame-list-z-order", Fpgtk_frame_list_z_order,
+       Spgtk_frame_list_z_order, 0, 1, 0,
+       doc: /* Return list of Emacs' frames, in Z (stacking) order.
+If TERMINAL is non-nil and specifies a live frame, return the child
+frames of that frame in Z (stacking) order.
+
+Frames are listed from topmost (first) to bottommost (last).  */)
+  (Lisp_Object terminal)
+{
+  Lisp_Object frames = Qnil;
+#if 0
+  PGTKWindow *parent = nil;
+
+  if (FRAMEP (terminal) && FRAME_LIVE_P (XFRAME (terminal)))
+    parent = [FRAME_PGTK_VIEW (XFRAME (terminal)) window];
+
+  for (PGTKWindow *win in [[NSApp orderedWindows] reverseObjectEnumerator])
+    {
+      Lisp_Object frame;
+
+      /* Check against [win parentWindow] so that it doesn't match itself. */
+      if (parent == nil || pgtk_window_is_ancestor (parent, [win 
parentWindow]))
+        {
+          XSETFRAME (frame, ((EmacsView *)[win delegate])->emacsframe);
+          frames = Fcons(frame, frames);
+        }
+    }
+#endif
+
+  return frames;
+}
+
+DEFUN ("pgtk-frame-restack", Fpgtk_frame_restack, Spgtk_frame_restack, 2, 3, 0,
+       doc: /* Restack FRAME1 below FRAME2.
+This means that if both frames are visible and the display areas of
+these frames overlap, FRAME2 (partially) obscures FRAME1.  If optional
+third argument ABOVE is non-nil, restack FRAME1 above FRAME2.  This
+means that if both frames are visible and the display areas of these
+frames overlap, FRAME1 (partially) obscures FRAME2.
+
+Some window managers may refuse to restack windows.  */)
+     (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object above)
+{
+  struct frame *f1 = decode_live_frame (frame1);
+  struct frame *f2 = decode_live_frame (frame2);
+
+  if (FRAME_PGTK_VIEW (f1) && FRAME_PGTK_VIEW (f2))
+    {
+#if 0
+      PGTKWindow *window = [FRAME_PGTK_VIEW (f1) window];
+      NSInteger window2 = [[FRAME_PGTK_VIEW (f2) window] windowNumber];
+      PGTKWindowOrderingMode flag = NILP (above) ? PGTKWindowBelow : 
PGTKWindowAbove;
+
+      [window orderWindow: flag
+               relativeTo: window2];
+
+#endif
+      return Qt;
+    }
+  else
+    {
+      error ("Cannot restack frames");
+      return Qnil;
+    }
+}
+
+const char *
+pgtk_get_defaults_value (const char *key)
+{
+  return NULL;
+}
+
+DEFUN ("pgtk-set-resource", Fpgtk_set_resource, Spgtk_set_resource, 3, 3, 0,
+       doc: /* Set property NAME of OWNER to VALUE, from the defaults database.
+If OWNER is nil, Emacs is assumed.
+If VALUE is nil, the default is removed.  */)
+     (Lisp_Object owner, Lisp_Object name, Lisp_Object value)
+{
+  check_window_system (NULL);
+  if (NILP (owner))
+    owner = build_string (pgtk_app_name);
+  CHECK_STRING (name);
+
+  return Qnil;
+}
+
+
+DEFUN ("x-server-max-request-size", Fx_server_max_request_size,
+       Sx_server_max_request_size,
+       0, 1, 0,
+       doc: /* This function is a no-op.  It is only present for completeness. 
 */)
+     (Lisp_Object terminal)
+{
+  check_pgtk_display_info (terminal);
+  /* This function has no real equivalent under PGTK.  Return nil to
+     indicate this. */
+  return Qnil;
+}
+
+
+DEFUN ("x-server-vendor", Fx_server_vendor, Sx_server_vendor, 0, 1, 0,
+       doc: /* Return the "vendor ID" string of the display server TERMINAL.
+\(Labeling every distributor as a "vendor" embodies the false assumption
+that operating systems cannot be developed and distributed noncommercially.)
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.  */)
+  (Lisp_Object terminal)
+{
+  check_pgtk_display_info (terminal);
+  return Qnil;
+}
+
+
+DEFUN ("x-server-version", Fx_server_version, Sx_server_version, 0, 1, 0,
+       doc: /* Return the version numbers of the server of display TERMINAL.
+The value is a list of three integers: the major and minor
+version numbers of the X Protocol in use, and the distributor-specific release
+number.  See also the function `x-server-vendor'.
+
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.  */)
+  (Lisp_Object terminal)
+{
+  check_pgtk_display_info (terminal);
+  /*NOTE: it is unclear what would best correspond with "protocol";
+          we return 10.3, meaning Panther, since this is roughly the
+          level that GNUstep's APIs correspond to.
+          The last number is where we distinguish between the Apple
+          and GNUstep implementations ("distributor-specific release
+          number") and give int'ized versions of major.minor. */
+  return list3i (0, 0, 0);
+}
+
+
+DEFUN ("x-display-screens", Fx_display_screens, Sx_display_screens, 0, 1, 0,
+       doc: /* Return the number of screens on the display server TERMINAL.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.
+
+Note: "screen" here is not in X11's.  For the number of physical monitors,
+ use `(length \(display-monitor-attributes-list TERMINAL))' instead.  */)
+  (Lisp_Object terminal)
+{
+  check_pgtk_display_info (terminal);
+  return make_fixnum (1);
+}
+
+
+DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, 0, 
1, 0,
+       doc: /* Return the height in millimeters of the the display TERMINAL.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.
+
+On \"multi-monitor\" setups this refers to the height in millimeters for
+all physical monitors associated with TERMINAL.  To get information
+for each physical monitor, use `display-monitor-attributes-list'.  */)
+  (Lisp_Object terminal)
+{
+  struct pgtk_display_info *dpyinfo = check_pgtk_display_info (terminal);
+  GdkDisplay *gdpy = dpyinfo->gdpy;
+  GdkMonitor *gmon = gdk_display_get_monitor_at_point(gdpy, 0, 0);
+  return make_fixnum (gdk_monitor_get_height_mm(gmon));
+}
+
+
+DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0,
+       doc: /* Return the width in millimeters of the the display TERMINAL.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.
+
+On \"multi-monitor\" setups this refers to the width in millimeters for
+all physical monitors associated with TERMINAL.  To get information
+for each physical monitor, use `display-monitor-attributes-list'.  */)
+  (Lisp_Object terminal)
+{
+  struct pgtk_display_info *dpyinfo = check_pgtk_display_info (terminal);
+  GdkDisplay *gdpy = dpyinfo->gdpy;
+  GdkMonitor *gmon = gdk_display_get_monitor_at_point(gdpy, 0, 0);
+  return make_fixnum (gdk_monitor_get_width_mm(gmon));
+}
+
+
+DEFUN ("x-display-backing-store", Fx_display_backing_store,
+       Sx_display_backing_store, 0, 1, 0,
+       doc: /* Return an indication of whether the the display TERMINAL does 
backing store.
+The value may be `buffered', `retained', or `non-retained'.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.  */)
+  (Lisp_Object terminal)
+{
+  check_pgtk_display_info (terminal);
+  return Qnil;
+}
+
+
+DEFUN ("x-display-visual-class", Fx_display_visual_class,
+       Sx_display_visual_class, 0, 1, 0,
+       doc: /* Return the visual class of the the display TERMINAL.
+The value is one of the symbols `static-gray', `gray-scale',
+`static-color', `pseudo-color', `true-color', or `direct-color'.
+
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.
+
+On PGTK, always return true-color.  */)
+  (Lisp_Object terminal)
+{
+  return intern ("true-color");
+}
+
+
+DEFUN ("x-display-save-under", Fx_display_save_under,
+       Sx_display_save_under, 0, 1, 0,
+       doc: /* Return t if TERMINAL supports the save-under feature.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.  */)
+  (Lisp_Object terminal)
+{
+  check_pgtk_display_info (terminal);
+  return Qnil;
+}
+
+
+DEFUN ("x-open-connection", Fx_open_connection, Sx_open_connection,
+       1, 3, 0,
+       doc: /* Open a connection to a display server.
+DISPLAY is the name of the display to connect to.
+Optional second arg XRM-STRING is a string of resources in xrdb format.
+If the optional third arg MUST-SUCCEED is non-nil,
+terminate Emacs if we can't open the connection.  */)
+     (Lisp_Object display, Lisp_Object resource_string, Lisp_Object 
must_succeed)
+{
+  struct pgtk_display_info *dpyinfo;
+
+  if (NILP(display))
+    display = build_string("");
+
+  CHECK_STRING (display);
+
+  nxatoms_of_pgtkselect ();
+  dpyinfo = pgtk_term_init (display, SSDATA(Vx_resource_name));
+  if (dpyinfo == 0)
+    {
+      if (!NILP (must_succeed))
+        fatal ("Display on %s not responding.\n",
+               SSDATA (display));
+      else
+        error ("Display on %s not responding.\n",
+               SSDATA (display));
+    }
+
+  return Qnil;
+}
+
+
+DEFUN ("x-close-connection", Fx_close_connection, Sx_close_connection,
+       1, 1, 0,
+       doc: /* Close the connection to TERMINAL's display server.
+For TERMINAL, specify a terminal object, a frame or a display name (a
+string).  If TERMINAL is nil, that stands for the selected frame's
+terminal.  */)
+     (Lisp_Object terminal)
+{
+  struct pgtk_display_info *dpyinfo = check_pgtk_display_info (terminal);
+
+  if (dpyinfo->reference_count > 0)
+    error ("Display still has frames on it");
+
+  pgtk_delete_terminal (dpyinfo->terminal);
+
+  return Qnil;
+}
+
+
+DEFUN ("x-display-list", Fx_display_list, Sx_display_list, 0, 0, 0,
+       doc: /* Return the list of display names that Emacs has connections to. 
 */)
+     (void)
+{
+  Lisp_Object result = Qnil;
+  struct pgtk_display_info *ndi;
+
+  for (ndi = x_display_list; ndi; ndi = ndi->next)
+    result = Fcons (XCAR (ndi->name_list_element), result);
+
+  return result;
+}
+
+
+DEFUN ("pgtk-hide-others", Fpgtk_hide_others, Spgtk_hide_others,
+       0, 0, 0,
+       doc: /* Hides all applications other than Emacs.  */)
+     (void)
+{
+  check_window_system (NULL);
+  return Qnil;
+}
+
+DEFUN ("pgtk-hide-emacs", Fpgtk_hide_emacs, Spgtk_hide_emacs,
+       1, 1, 0,
+       doc: /* If ON is non-nil, the entire Emacs application is hidden.
+Otherwise if Emacs is hidden, it is unhidden.
+If ON is equal to `activate', Emacs is unhidden and becomes
+the active application.  */)
+     (Lisp_Object on)
+{
+  check_window_system (NULL);
+  return Qnil;
+}
+
+
+DEFUN ("pgtk-font-name", Fpgtk_font_name, Spgtk_font_name, 1, 1, 0,
+       doc: /* Determine font PostScript or family name for font NAME.
+NAME should be a string containing either the font name or an XLFD
+font descriptor.  If string contains `fontset' and not
+`fontset-startup', it is left alone. */)
+     (Lisp_Object name)
+{
+  char *nm;
+  CHECK_STRING (name);
+  nm = SSDATA (name);
+
+  if (nm[0] != '-')
+    return name;
+  if (strstr (nm, "fontset") && !strstr (nm, "fontset-startup"))
+    return name;
+
+  char *str = pgtk_xlfd_to_fontname (SSDATA (name));
+  name = build_string (str);
+  xfree(str);
+  return name;
+}
+
+/* ==========================================================================
+
+    Miscellaneous functions not called through hooks
+
+   ========================================================================== 
*/
+
+/* called from frame.c */
+struct pgtk_display_info *
+check_x_display_info (Lisp_Object frame)
+{
+  return check_pgtk_display_info (frame);
+}
+
+
+void
+x_set_scroll_bar_default_width (struct frame *f)
+{
+  int unit = FRAME_COLUMN_WIDTH (f);
+  int minw = xg_get_default_scrollbar_width (f);
+  /* A minimum width of 14 doesn't look good for toolkit scroll bars.  */
+  FRAME_CONFIG_SCROLL_BAR_COLS (f) = (minw + unit - 1) / unit;
+  FRAME_CONFIG_SCROLL_BAR_WIDTH (f) = minw;
+}
+
+void
+x_set_scroll_bar_default_height (struct frame *f)
+{
+  int height = FRAME_LINE_HEIGHT (f);
+  int min_height = xg_get_default_scrollbar_height (f);
+  /* A minimum height of 14 doesn't look good for toolkit scroll bars.  */
+  FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) = min_height;
+  FRAME_CONFIG_SCROLL_BAR_LINES (f) = (min_height + height - 1) / height;
+}
+
+/* terms impl this instead of x-get-resource directly */
+char *
+x_get_string_resource (XrmDatabase rdb, const char *name, const char *class)
+{
+  /* remove appname prefix; TODO: allow for !="Emacs" */
+  const char *res, *toCheck = class + (!strncmp (class, "Emacs.", 6) ? 6 : 0);
+
+  check_window_system (NULL);
+
+  if (inhibit_x_resources)
+    /* --quick was passed, so this is a no-op.  */
+    return NULL;
+
+  res = pgtk_get_defaults_value (toCheck);
+  return (char *) (!res ? NULL
+                  : !c_strncasecmp (res, "YES", 3) ? "true"
+                  : !c_strncasecmp (res, "NO", 2) ? "false"
+                  : res);
+}
+
+
+Lisp_Object
+x_get_focus_frame (struct frame *frame)
+{
+  struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
+  Lisp_Object focus;
+
+  if (!dpyinfo->x_focus_frame)
+    return Qnil;
+
+  XSETFRAME (focus, dpyinfo->x_focus_frame);
+  return focus;
+}
+
+/* ==========================================================================
+
+    Lisp definitions that, for whatever reason, we can't alias as 'ns-XXX'.
+
+   ========================================================================== 
*/
+
+
+DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0,
+       doc: /* Internal function called by `color-defined-p', which see.  */)
+     (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  return pgtk_lisp_to_color (color, &col) ? Qnil : Qt;
+}
+
+
+DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, 0,
+       doc: /* Internal function called by `color-values', which see.  */)
+     (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+
+  CHECK_STRING (color);
+
+  block_input ();
+
+  if (pgtk_lisp_to_color (color, &col))
+    {
+      unblock_input ();
+      return Qnil;
+    }
+
+  unblock_input ();
+
+  return list3i (col.red, col.green, col.blue);
+}
+
+
+DEFUN ("xw-display-color-p", Fxw_display_color_p, Sxw_display_color_p, 0, 1, 0,
+       doc: /* Internal function called by `display-color-p', which see.  */)
+     (Lisp_Object terminal)
+{
+  check_pgtk_display_info (terminal);
+  return Qt;
+}
+
+
+DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p, Sx_display_grayscale_p,
+       0, 1, 0,
+       doc: /* Return t if the display supports shades of gray.
+Note that color displays do support shades of gray.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.  */)
+  (Lisp_Object terminal)
+{
+  return Qnil;
+}
+
+
+DEFUN ("x-display-pixel-width", Fx_display_pixel_width, Sx_display_pixel_width,
+       0, 1, 0,
+       doc: /* Return the width in pixels of the display TERMINAL.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.
+
+On \"multi-monitor\" setups this refers to the pixel width for all
+physical monitors associated with TERMINAL.  To get information for
+each physical monitor, use `display-monitor-attributes-list'.  */)
+  (Lisp_Object terminal)
+{
+  struct pgtk_display_info *dpyinfo = check_pgtk_display_info (terminal);
+
+  return make_fixnum (x_display_pixel_width (dpyinfo));
+}
+
+
+DEFUN ("x-display-pixel-height", Fx_display_pixel_height,
+       Sx_display_pixel_height, 0, 1, 0,
+       doc: /* Return the height in pixels of the display TERMINAL.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.
+
+On \"multi-monitor\" setups this refers to the pixel height for all
+physical monitors associated with TERMINAL.  To get information for
+each physical monitor, use `display-monitor-attributes-list'.  */)
+  (Lisp_Object terminal)
+{
+  struct pgtk_display_info *dpyinfo = check_pgtk_display_info (terminal);
+
+  return make_fixnum (x_display_pixel_height (dpyinfo));
+}
+
+DEFUN ("pgtk-display-monitor-attributes-list",
+       Fpgtk_display_monitor_attributes_list,
+       Spgtk_display_monitor_attributes_list,
+       0, 1, 0,
+       doc: /* Return a list of physical monitor attributes on the X display 
TERMINAL.
+
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.
+
+Internal use only, use `display-monitor-attributes-list' instead.  */)
+  (Lisp_Object terminal)
+{
+  struct terminal *term = decode_live_terminal (terminal);
+  struct pgtk_display_info *dpyinfo = check_pgtk_display_info (terminal);
+  GdkDisplay *gdpy = dpyinfo->gdpy;
+  GdkMonitor **gmons;
+  int i, n_monitors, primary_index;
+  struct MonitorInfo *monitors;
+  Lisp_Object monitor_frames = Qnil;
+  Lisp_Object frame = Qnil, rest = Qnil;
+  Lisp_Object rv = Qnil;
+
+  if (term->type != output_pgtk)
+    return Qnil;
+
+  n_monitors = gdk_display_get_n_monitors(gdpy);
+  if (n_monitors == 0)
+    return Qnil;
+
+  gmons = xmalloc(sizeof *gmons * n_monitors);
+  for (i = 0; i < n_monitors; i++)
+    gmons[i] = gdk_display_get_monitor(gdpy, i);
+
+  monitors = xzalloc (n_monitors * sizeof *monitors);
+  for (i = 0; i < n_monitors; i++) {
+    struct MonitorInfo *mon = &monitors[i];
+    GdkMonitor *gmon = gmons[i];
+    if (gmon != NULL) {
+      GdkRectangle geom;
+      gdk_monitor_get_geometry(gmon, &geom);
+      mon->geom.x = geom.x;
+      mon->geom.y = geom.y;
+      mon->geom.width = geom.width;
+      mon->geom.height = geom.height;
+
+      gdk_monitor_get_workarea(gmon, &geom);
+      mon->work.x = geom.x;
+      mon->work.y = geom.y;
+      mon->work.width = geom.width;
+      mon->work.height = geom.height;
+
+      mon->mm_width = gdk_monitor_get_width_mm(gmon);
+      mon->mm_height = gdk_monitor_get_height_mm(gmon);
+
+      mon->name = xstrdup(gdk_monitor_get_model(gmon));
+    }
+  }
+
+  monitor_frames = Fmake_vector (make_fixnum (n_monitors), Qnil);
+  FOR_EACH_FRAME (rest, frame)
+    {
+      struct frame *f = XFRAME (frame);
+
+      if (FRAME_PGTK_P (f))
+       {
+         GtkWidget *widget = FRAME_GTK_WIDGET(f);
+         GdkMonitor *gmon = gdk_display_get_monitor_at_window(gdpy, 
gtk_widget_get_window(widget));
+
+         if (gmon != NULL) {
+           for (i = 0; i < n_monitors; i++) {
+             if (gmons[i] == gmon) {
+               ASET (monitor_frames, i, Fcons (frame, AREF (monitor_frames, 
i)));
+               break;
+             }
+           }
+         }
+       }
+    }
+
+  primary_index = -1;
+  for (i = 0; i < n_monitors; i++) {
+    if (gmons[i] != NULL && gdk_monitor_is_primary(gmons[i])) {
+      primary_index = i;
+      break;
+    }
+  }
+
+  rv = make_monitor_attribute_list(monitors, n_monitors, primary_index, 
monitor_frames, "Gdk");
+
+  free_monitors(monitors, n_monitors);
+  xfree(gmons);
+
+  return rv;
+}
+
+
+DEFUN ("x-display-planes", Fx_display_planes, Sx_display_planes,
+       0, 1, 0,
+       doc: /* Return the number of bitplanes of the display TERMINAL.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.  */)
+  (Lisp_Object terminal)
+{
+  check_pgtk_display_info (terminal);
+  return make_fixnum(32);
+}
+
+
+DEFUN ("x-display-color-cells", Fx_display_color_cells, Sx_display_color_cells,
+       0, 1, 0,
+       doc: /* Returns the number of color cells of the display TERMINAL.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.  */)
+  (Lisp_Object terminal)
+{
+  struct pgtk_display_info *dpyinfo = check_pgtk_display_info (terminal);
+  /* We force 24+ bit depths to 24-bit to prevent an overflow.  */
+  return make_fixnum (1 << min (dpyinfo->n_planes, 24));
+}
+
+/***********************************************************************
+                               Tool tips
+ ***********************************************************************/
+
+/* The frame of a currently visible tooltip.  */
+
+Lisp_Object tip_frame;
+
+/* If non-nil, a timer started that hides the last tooltip when it
+   fires.  */
+
+static Lisp_Object tip_timer;
+
+/* Compute where to display tip frame F.  PARMS is the list of frame
+   parameters for F.  DX and DY are specified offsets from the current
+   location of the mouse.  WIDTH and HEIGHT are the width and height
+   of the tooltip.  Return coordinates relative to the root window of
+   the display in *ROOT_X, and *ROOT_Y.  */
+
+static void
+compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx, 
Lisp_Object dy, int width, int height, int *root_x, int *root_y)
+{
+  Lisp_Object left, top, right, bottom;
+  int min_x, min_y, max_x, max_y = -1;
+
+  /* User-specified position?  */
+  left = Fcdr (Fassq (Qleft, parms));
+  top  = Fcdr (Fassq (Qtop, parms));
+  right = Fcdr (Fassq (Qright, parms));
+  bottom = Fcdr (Fassq (Qbottom, parms));
+
+  /* Move the tooltip window where the mouse pointer is.  Resize and
+     show it.  */
+  if ((!INTEGERP (left) && !INTEGERP (right))
+      || (!INTEGERP (top) && !INTEGERP (bottom)))
+    {
+      Lisp_Object frame, attributes, monitor, geometry;
+      GdkSeat *seat = 
gdk_display_get_default_seat(FRAME_DISPLAY_INFO(f)->gdpy);
+      GdkDevice *dev = gdk_seat_get_pointer(seat);
+      GdkScreen *scr;
+
+      block_input ();
+      gdk_device_get_position(dev, &scr, root_x, root_y);
+      unblock_input ();
+
+      XSETFRAME(frame, f);
+      attributes = Fpgtk_display_monitor_attributes_list (frame);
+
+      /* Try to determine the monitor where the mouse pointer is and
+         its geometry.  See bug#22549.  */
+      while (CONSP (attributes))
+       {
+          monitor = XCAR (attributes);
+          geometry = Fassq (Qgeometry, monitor);
+          if (CONSP (geometry))
+            {
+              min_x = XFIXNUM (Fnth (make_fixnum (1), geometry));
+              min_y = XFIXNUM (Fnth (make_fixnum (2), geometry));
+              max_x = min_x + XFIXNUM (Fnth (make_fixnum (3), geometry));
+              max_y = min_y + XFIXNUM (Fnth (make_fixnum (4), geometry));
+              if (min_x <= *root_x && *root_x < max_x
+                  && min_y <= *root_y && *root_y < max_y)
+                {
+                  break;
+                }
+              max_y = -1;
+            }
+
+          attributes = XCDR (attributes);
+       }
+    }
+
+  /* It was not possible to determine the monitor's geometry, so we
+     assign some sane defaults here: */
+  if ( max_y < 0 )
+    {
+      min_x = 0;
+      min_y = 0;
+      max_x = x_display_pixel_width (FRAME_DISPLAY_INFO (f));
+      max_y = x_display_pixel_height (FRAME_DISPLAY_INFO (f));
+    }
+
+  if (INTEGERP (top))
+    *root_y = XFIXNUM (top);
+  else if (INTEGERP (bottom))
+    *root_y = XFIXNUM (bottom) - height;
+  else if (*root_y + XFIXNUM (dy) <= min_y)
+    *root_y = min_y; /* Can happen for negative dy */
+  else if (*root_y + XFIXNUM (dy) + height <= max_y)
+    /* It fits below the pointer */
+    *root_y += XFIXNUM (dy);
+  else if (height + XFIXNUM (dy) + min_y <= *root_y)
+    /* It fits above the pointer.  */
+    *root_y -= height + XFIXNUM (dy);
+  else
+    /* Put it on the top.  */
+    *root_y = min_y;
+
+  if (INTEGERP (left))
+    *root_x = XFIXNUM (left);
+  else if (INTEGERP (right))
+    *root_x = XFIXNUM (right) - width;
+  else if (*root_x + XFIXNUM (dx) <= min_x)
+    *root_x = 0; /* Can happen for negative dx */
+  else if (*root_x + XFIXNUM (dx) + width <= max_x)
+    /* It fits to the right of the pointer.  */
+    *root_x += XFIXNUM (dx);
+  else if (width + XFIXNUM (dx) + min_x <= *root_x)
+    /* It fits to the left of the pointer.  */
+    *root_x -= width + XFIXNUM (dx);
+  else
+    /* Put it left justified on the screen -- it ought to fit that way.  */
+    *root_x = min_x;
+}
+
+
+/* Hide tooltip.  Delete its frame if DELETE is true.  */
+static Lisp_Object
+x_hide_tip (bool delete)
+{
+  if (!NILP (tip_timer))
+    {
+      call1 (Qcancel_timer, tip_timer);
+      tip_timer = Qnil;
+    }
+
+  if (NILP (tip_frame)
+      || (!delete && FRAMEP (tip_frame)
+         && !FRAME_VISIBLE_P (XFRAME (tip_frame))))
+    return Qnil;
+  else
+    {
+      ptrdiff_t count;
+      Lisp_Object was_open = Qnil;
+
+      count = SPECPDL_INDEX ();
+      specbind (Qinhibit_redisplay, Qt);
+      specbind (Qinhibit_quit, Qt);
+
+      {
+       /* When using system tooltip, tip_frame is the Emacs frame on
+          which the tip is shown.  */
+       struct frame *f = XFRAME (tip_frame);
+
+       if (FRAME_LIVE_P (f) && xg_hide_tooltip (f))
+         {
+           tip_frame = Qnil;
+           was_open = Qt;
+         }
+      }
+
+      if (FRAMEP (tip_frame))
+       {
+         if (delete)
+           {
+             delete_frame (tip_frame, Qnil);
+             tip_frame = Qnil;
+           }
+         else
+           x_make_frame_invisible (XFRAME (tip_frame));
+
+         was_open = Qt;
+       }
+      else
+       tip_frame = Qnil;
+
+      return unbind_to (count, was_open);
+    }
+}
+
+DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
+       doc: /* Show STRING in a "tooltip" window on frame FRAME.
+A tooltip window is a small X window displaying a string.
+
+This is an internal function; Lisp code should call `tooltip-show'.
+
+FRAME nil or omitted means use the selected frame.
+
+PARMS is an optional list of frame parameters which can be used to
+change the tooltip's appearance.
+
+Automatically hide the tooltip after TIMEOUT seconds.  TIMEOUT nil
+means use the default timeout of 5 seconds.
+
+If the list of frame parameters PARMS contains a `left' parameter,
+display the tooltip at that x-position.  If the list of frame parameters
+PARMS contains no `left' but a `right' parameter, display the tooltip
+right-adjusted at that x-position. Otherwise display it at the
+x-position of the mouse, with offset DX added (default is 5 if DX isn't
+specified).
+
+Likewise for the y-position: If a `top' frame parameter is specified, it
+determines the position of the upper edge of the tooltip window.  If a
+`bottom' parameter but no `top' frame parameter is specified, it
+determines the position of the lower edge of the tooltip window.
+Otherwise display the tooltip window at the y-position of the mouse,
+with offset DY added (default is -10).
+
+A tooltip's maximum size is specified by `x-max-tooltip-size'.
+Text larger than the specified size is clipped.  */)
+  (Lisp_Object string, Lisp_Object frame, Lisp_Object parms, Lisp_Object 
timeout, Lisp_Object dx, Lisp_Object dy)
+{
+  struct frame *f;
+  int root_x, root_y;
+  int width, height;
+  ptrdiff_t count = SPECPDL_INDEX ();
+
+  specbind (Qinhibit_redisplay, Qt);
+
+  CHECK_STRING (string);
+  if (SCHARS (string) == 0)
+    string = make_unibyte_string (" ", 1);
+
+  f = decode_window_system_frame (frame);
+  if (NILP (timeout))
+    timeout = make_fixnum (5);
+  else
+    CHECK_FIXNAT (timeout);
+
+  if (NILP (dx))
+    dx = make_fixnum (5);
+  else
+    CHECK_NUMBER (dx);
+
+  if (NILP (dy))
+    dy = make_fixnum (-10);
+  else
+    CHECK_NUMBER (dy);
+
+  {
+    bool ok;
+
+    /* Hide a previous tip, if any.  */
+    Fx_hide_tip ();
+
+    block_input ();
+    ok = xg_prepare_tooltip (f, string, &width, &height);
+    if (ok)
+      {
+       compute_tip_xy (f, parms, dx, dy, width, height, &root_x, &root_y);
+       xg_show_tooltip (f, root_x, root_y);
+       /* This is used in Fx_hide_tip.  */
+       XSETFRAME (tip_frame, f);
+      }
+    unblock_input ();
+    if (ok) goto start_timer;
+  }
+
+ start_timer:
+  /* Let the tip disappear after timeout seconds.  */
+  tip_timer = call3 (intern ("run-at-time"), timeout, Qnil,
+                    intern ("x-hide-tip"));
+
+  return unbind_to (count, Qnil);
+}
+
+
+DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0,
+       doc: /* Hide the current tooltip window, if there is any.
+Value is t if tooltip was open, nil otherwise.  */)
+  (void)
+{
+  return x_hide_tip (!tooltip_reuse_hidden_frame);
+}
+
+/* Return geometric attributes of FRAME.  According to the value of
+   ATTRIBUTES return the outer edges of FRAME (Qouter_edges), the inner
+   edges of FRAME, the root window edges of frame (Qroot_edges).  Any
+   other value means to return the geometry as returned by
+   Fx_frame_geometry.  */
+static Lisp_Object
+frame_geometry (Lisp_Object frame, Lisp_Object attribute)
+{
+  struct frame *f = decode_live_frame (frame);
+  Lisp_Object fullscreen_symbol = Fframe_parameter (frame, Qfullscreen);
+  bool fullscreen = (EQ (fullscreen_symbol, Qfullboth)
+                    || EQ (fullscreen_symbol, Qfullscreen));
+  int border = fullscreen ? 0 : f->border_width;
+  int title_height = 0;
+  int native_width = FRAME_PIXEL_WIDTH (f);
+  int native_height = FRAME_PIXEL_HEIGHT (f);
+  int outer_width = native_width + 2 * border;
+  int outer_height = native_height + 2 * border + title_height;
+  int native_left = f->left_pos + border;
+  int native_top = f->top_pos + border + title_height;
+  int native_right = f->left_pos + outer_width - border;
+  int native_bottom = f->top_pos + outer_height - border;
+  int internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f);
+  int tool_bar_height = FRAME_TOOLBAR_HEIGHT (f);
+  int tool_bar_width = (tool_bar_height
+                       ? outer_width - 2 * internal_border_width
+                       : 0);
+
+  /* Construct list.  */
+  if (EQ (attribute, Qouter_edges))
+    return list4 (make_fixnum (f->left_pos), make_fixnum (f->top_pos),
+                 make_fixnum (f->left_pos + outer_width),
+                 make_fixnum (f->top_pos + outer_height));
+  else if (EQ (attribute, Qnative_edges))
+    return list4 (make_fixnum (native_left), make_fixnum (native_top),
+                 make_fixnum (native_right), make_fixnum (native_bottom));
+  else if (EQ (attribute, Qinner_edges))
+    return list4 (make_fixnum (native_left + internal_border_width),
+                 make_fixnum (native_top
+                              + tool_bar_height
+                              + internal_border_width),
+                 make_fixnum (native_right - internal_border_width),
+                 make_fixnum (native_bottom - internal_border_width));
+  else
+    return
+      listn (CONSTYPE_HEAP, 10,
+            Fcons (Qouter_position,
+                   Fcons (make_fixnum (f->left_pos),
+                          make_fixnum (f->top_pos))),
+            Fcons (Qouter_size,
+                   Fcons (make_fixnum (outer_width),
+                          make_fixnum (outer_height))),
+            Fcons (Qexternal_border_size,
+                   (fullscreen
+                    ? Fcons (make_fixnum (0), make_fixnum (0))
+                    : Fcons (make_fixnum (border), make_fixnum (border)))),
+            Fcons (Qtitle_bar_size,
+                   Fcons (make_fixnum (0), make_fixnum (title_height))),
+            Fcons (Qmenu_bar_external, Qnil),
+            Fcons (Qmenu_bar_size, Fcons (make_fixnum (0), make_fixnum (0))),
+            Fcons (Qtool_bar_external,
+                   FRAME_EXTERNAL_TOOL_BAR (f) ? Qt : Qnil),
+            Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)),
+            Fcons (Qtool_bar_size,
+                   Fcons (make_fixnum (tool_bar_width),
+                          make_fixnum (tool_bar_height))),
+            Fcons (Qinternal_border_width,
+                   make_fixnum (internal_border_width)));
+}
+
+DEFUN ("pgtk-frame-geometry", Fpgtk_frame_geometry, Spgtk_frame_geometry, 0, 
1, 0,
+       doc: /* Return geometric attributes of FRAME.
+FRAME must be a live frame and defaults to the selected one.  The return
+value is an association list of the attributes listed below.  All height
+and width values are in pixels.
+
+`outer-position' is a cons of the outer left and top edges of FRAME
+  relative to the origin - the position (0, 0) - of FRAME's display.
+
+`outer-size' is a cons of the outer width and height of FRAME.  The
+  outer size includes the title bar and the external borders as well as
+  any menu and/or tool bar of frame.
+
+`external-border-size' is a cons of the horizontal and vertical width of
+  FRAME's external borders as supplied by the window manager.
+
+`title-bar-size' is a cons of the width and height of the title bar of
+  FRAME as supplied by the window manager.  If both of them are zero,
+  FRAME has no title bar.  If only the width is zero, Emacs was not
+  able to retrieve the width information.
+
+`menu-bar-external', if non-nil, means the menu bar is external (never
+  included in the inner edges of FRAME).
+
+`menu-bar-size' is a cons of the width and height of the menu bar of
+  FRAME.
+
+`tool-bar-external', if non-nil, means the tool bar is external (never
+  included in the inner edges of FRAME).
+
+`tool-bar-position' tells on which side the tool bar on FRAME is and can
+  be one of `left', `top', `right' or `bottom'.  If this is nil, FRAME
+  has no tool bar.
+
+`tool-bar-size' is a cons of the width and height of the tool bar of
+  FRAME.
+
+`internal-border-width' is the width of the internal border of
+  FRAME.  */)
+  (Lisp_Object frame)
+{
+  return frame_geometry (frame, Qnil);
+}
+
+DEFUN ("pgtk-frame-edges", Fpgtk_frame_edges, Spgtk_frame_edges, 0, 2, 0,
+       doc: /* Return edge coordinates of FRAME.
+FRAME must be a live frame and defaults to the selected one.  The return
+value is a list of the form (LEFT, TOP, RIGHT, BOTTOM).  All values are
+in pixels relative to the origin - the position (0, 0) - of FRAME's
+display.
+
+If optional argument TYPE is the symbol `outer-edges', return the outer
+edges of FRAME.  The outer edges comprise the decorations of the window
+manager (like the title bar or external borders) as well as any external
+menu or tool bar of FRAME.  If optional argument TYPE is the symbol
+`native-edges' or nil, return the native edges of FRAME.  The native
+edges exclude the decorations of the window manager and any external
+menu or tool bar of FRAME.  If TYPE is the symbol `inner-edges', return
+the inner edges of FRAME.  These edges exclude title bar, any borders,
+menu bar or tool bar of FRAME.  */)
+  (Lisp_Object frame, Lisp_Object type)
+{
+  return frame_geometry (frame, ((EQ (type, Qouter_edges)
+                                 || EQ (type, Qinner_edges))
+                                ? type
+                                : Qnative_edges));
+}
+
+DEFUN ("pgtk-set-mouse-absolute-pixel-position",
+       Fpgtk_set_mouse_absolute_pixel_position,
+       Spgtk_set_mouse_absolute_pixel_position, 2, 2, 0,
+       doc: /* Move mouse pointer to absolute pixel position (X, Y).
+The coordinates X and Y are interpreted in pixels relative to a position
+\(0, 0) of the selected frame's display.  */)
+       (Lisp_Object x, Lisp_Object y)
+{
+  struct frame *f = SELECTED_FRAME ();
+  GtkWidget *widget = FRAME_GTK_OUTER_WIDGET(f);
+  GdkWindow *window = gtk_widget_get_window(widget);
+  GdkDisplay *gdpy = gdk_window_get_display(window);
+  GdkScreen *gscr = gdk_window_get_screen(window);
+  GdkSeat *seat = gdk_display_get_default_seat(gdpy);
+  GdkDevice *device = gdk_seat_get_pointer(seat);
+
+  PGTK_TRACE("pgtk-set-mouse-absolute-pixel-position:");
+  gdk_device_warp(device, gscr, XFIXNUM(x), XFIXNUM(y));  /* No effect on 
wayland. */
+
+  return Qnil;
+}
+
+DEFUN ("pgtk-mouse-absolute-pixel-position",
+       Fpgtk_mouse_absolute_pixel_position,
+       Spgtk_mouse_absolute_pixel_position, 0, 0, 0,
+       doc: /* Return absolute position of mouse cursor in pixels.
+The position is returned as a cons cell (X . Y) of the
+coordinates of the mouse cursor position in pixels relative to a
+position (0, 0) of the selected frame's terminal. */)
+     (void)
+{
+  struct frame *f = SELECTED_FRAME ();
+  GtkWidget *widget = FRAME_GTK_OUTER_WIDGET(f);
+  GdkWindow *window = gtk_widget_get_window(widget);
+  GdkDisplay *gdpy = gdk_window_get_display(window);
+  GdkScreen *gscr;
+  GdkSeat *seat = gdk_display_get_default_seat(gdpy);
+  GdkDevice *device = gdk_seat_get_pointer(seat);
+  int x = 0, y = 0;
+
+  gdk_device_get_position(device, &gscr, &x, &y);  /* can't get on wayland? */
+
+  return Fcons(make_fixnum(x), make_fixnum(y));
+}
+
+
+DEFUN ("pgtk-page-setup-dialog", Fpgtk_page_setup_dialog, 
Spgtk_page_setup_dialog, 0, 0, 0,
+       doc: /* Pop up a page setup dialog.
+The current page setup can be obtained using `x-get-page-setup'.  */)
+     (void)
+{
+  block_input ();
+  xg_page_setup_dialog ();
+  unblock_input ();
+
+  return Qnil;
+}
+
+DEFUN ("pgtk-get-page-setup", Fpgtk_get_page_setup, Spgtk_get_page_setup, 0, 
0, 0,
+       doc: /* Return the value of the current page setup.
+The return value is an alist containing the following keys:
+
+  orientation: page orientation (symbol `portrait', `landscape',
+       `reverse-portrait', or `reverse-landscape').
+  width, height: page width/height in points not including margins.
+  left-margin, right-margin, top-margin, bottom-margin: print margins,
+       which is the parts of the page that the printer cannot print
+       on, in points.
+
+The paper width can be obtained as the sum of width, left-margin, and
+right-margin values if the page orientation is `portrait' or
+`reverse-portrait'.  Otherwise, it is the sum of width, top-margin,
+and bottom-margin values.  Likewise, the paper height is the sum of
+height, top-margin, and bottom-margin values if the page orientation
+is `portrait' or `reverse-portrait'.  Otherwise, it is the sum of
+height, left-margin, and right-margin values.  */)
+     (void)
+{
+  Lisp_Object result;
+
+  block_input ();
+  result = xg_get_page_setup ();
+  unblock_input ();
+
+  return result;
+}
+
+DEFUN ("pgtk-print-frames-dialog", Fpgtk_print_frames_dialog, 
Spgtk_print_frames_dialog, 0, 1, "",
+       doc: /* Pop up a print dialog to print the current contents of FRAMES.
+FRAMES should be nil (the selected frame), a frame, or a list of
+frames (each of which corresponds to one page).  Each frame should be
+visible.  */)
+     (Lisp_Object frames)
+{
+  Lisp_Object rest, tmp;
+  int count;
+
+  if (!CONSP (frames))
+    frames = list1 (frames);
+
+  tmp = Qnil;
+  for (rest = frames; CONSP (rest); rest = XCDR (rest))
+    {
+      struct frame *f = decode_window_system_frame (XCAR (rest));
+      Lisp_Object frame;
+
+      XSETFRAME (frame, f);
+      if (!FRAME_VISIBLE_P (f))
+       error ("Frames to be printed must be visible.");
+      tmp = Fcons (frame, tmp);
+    }
+  frames = Fnreverse (tmp);
+
+  /* Make sure the current matrices are up-to-date.  */
+  count = SPECPDL_INDEX ();
+  specbind (Qredisplay_dont_pause, Qt);
+  redisplay_preserve_echo_area (32);
+  unbind_to (count, Qnil);
+
+  block_input ();
+  xg_print_frames_dialog (frames);
+  unblock_input ();
+
+  return Qnil;
+}
+
+DEFUN ("pgtk-backend-display-class", Fpgtk_backend_display_class, 
Spgtk_backend_display_class,
+       0, 1, "",
+       doc: /* Returns the name of the Gdk backend display class of the 
TERMINAL.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.  */)
+  (Lisp_Object terminal)
+{
+  struct pgtk_display_info *dpyinfo = check_pgtk_display_info (terminal);
+  GdkDisplay *gdpy = dpyinfo->gdpy;
+  const gchar *type_name = G_OBJECT_TYPE_NAME(G_OBJECT(gdpy));
+  return build_string(type_name);
+}
+
+/* ==========================================================================
+
+    Lisp interface declaration
+
+   ========================================================================== 
*/
+
+void
+syms_of_pgtkfns (void)
+{
+  DEFSYM (Qfont_parameter, "font-parameter");
+  DEFSYM (Qfontsize, "fontsize");
+  DEFSYM (Qcancel_timer, "cancel-timer");
+  DEFSYM (Qframe_title_format, "frame-title-format");
+  DEFSYM (Qicon_title_format, "icon-title-format");
+  DEFSYM (Qdark, "dark");
+
+  DEFVAR_LISP ("pgtk-icon-type-alist", Vpgtk_icon_type_alist,
+               doc: /* Alist of elements (REGEXP . IMAGE) for images of icons 
associated to frames.
+If the title of a frame matches REGEXP, then IMAGE.tiff is
+selected as the image of the icon representing the frame when it's
+miniaturized.  If an element is t, then Emacs tries to select an icon
+based on the filetype of the visited file.
+
+The images have to be installed in a folder called English.lproj in the
+Emacs folder.  You have to restart Emacs after installing new icons.
+
+Example: Install an icon Gnus.tiff and execute the following code
+
+  (setq pgtk-icon-type-alist
+        (append pgtk-icon-type-alist
+                \\='((\"^\\\\*\\\\(Group\\\\*$\\\\|Summary 
\\\\|Article\\\\*$\\\\)\"
+                   . \"Gnus\"))))
+
+When you miniaturize a Group, Summary or Article frame, Gnus.tiff will
+be used as the image of the icon representing the frame.  */);
+  Vpgtk_icon_type_alist = list1 (Qt);
+
+
+  /* Provide x-toolkit also for GTK.  Internally GTK does not use Xt so it
+     is not an X toolkit in that sense (USE_X_TOOLKIT is not defined).
+     But for a user it is a toolkit for X, and indeed, configure
+     accepts --with-x-toolkit=gtk.  */
+  Fprovide (intern_c_string ("x-toolkit"), Qnil);
+  Fprovide (intern_c_string ("gtk"), Qnil);
+  Fprovide (intern_c_string ("move-toolbar"), Qnil);
+
+  DEFVAR_LISP ("gtk-version-string", Vgtk_version_string,
+               doc: /* Version info for GTK+.  */);
+  {
+    char *ver = g_strdup_printf("%d.%d.%d",
+                               GTK_MAJOR_VERSION, GTK_MINOR_VERSION, 
GTK_MICRO_VERSION);
+    int len = strlen(ver);
+    Vgtk_version_string = make_pure_string (ver, len, len, false);
+    g_free(ver);
+  }
+
+
+  Fprovide (intern_c_string ("cairo"), Qnil);
+
+  DEFVAR_LISP ("cairo-version-string", Vcairo_version_string,
+               doc: /* Version info for cairo.  */);
+  {
+    char *ver = g_strdup_printf("%d.%d.%d",
+                               CAIRO_VERSION_MAJOR, CAIRO_VERSION_MINOR,
+                               CAIRO_VERSION_MICRO);
+    int len = strlen(ver);
+    Vcairo_version_string = make_pure_string (ver, len, len, false);
+    g_free(ver);
+  }
+
+
+  defsubr (&Spgtk_set_resource);
+  defsubr (&Sxw_display_color_p); /* this and next called directly by C code */
+  defsubr (&Sx_display_grayscale_p);
+  defsubr (&Spgtk_font_name);
+  defsubr (&Sxw_color_defined_p);
+  defsubr (&Sxw_color_values);
+  defsubr (&Sx_server_max_request_size);
+  defsubr (&Sx_server_vendor);
+  defsubr (&Sx_server_version);
+  defsubr (&Sx_display_pixel_width);
+  defsubr (&Sx_display_pixel_height);
+  defsubr (&Spgtk_display_monitor_attributes_list);
+  defsubr (&Spgtk_frame_geometry);
+  defsubr (&Spgtk_frame_edges);
+  defsubr (&Spgtk_frame_list_z_order);
+  defsubr (&Spgtk_frame_restack);
+  defsubr (&Spgtk_set_mouse_absolute_pixel_position);
+  defsubr (&Spgtk_mouse_absolute_pixel_position);
+  defsubr (&Sx_display_mm_width);
+  defsubr (&Sx_display_mm_height);
+  defsubr (&Sx_display_screens);
+  defsubr (&Sx_display_planes);
+  defsubr (&Sx_display_color_cells);
+  defsubr (&Sx_display_visual_class);
+  defsubr (&Sx_display_backing_store);
+  defsubr (&Sx_display_save_under);
+  defsubr (&Sx_create_frame);
+  defsubr (&Sx_open_connection);
+  defsubr (&Sx_close_connection);
+  defsubr (&Sx_display_list);
+
+  defsubr (&Spgtk_hide_others);
+  defsubr (&Spgtk_hide_emacs);
+
+  defsubr (&Sx_show_tip);
+  defsubr (&Sx_hide_tip);
+
+  // defsubr (&Spgtk_export_frames);
+  defsubr (&Spgtk_page_setup_dialog);
+  defsubr (&Spgtk_get_page_setup);
+  defsubr (&Spgtk_print_frames_dialog);
+  defsubr (&Spgtk_backend_display_class);
+
+  as_status = 0;
+  as_script = Qnil;
+  as_result = 0;
+
+  tip_frame = Qnil;
+  staticpro(&tip_frame);
+  tip_timer = Qnil;
+  staticpro(&tip_timer);
+
+/* This is not ifdef:ed, so other builds than GTK can customize it.  */
+  DEFVAR_BOOL ("x-gtk-use-old-file-dialog", x_gtk_use_old_file_dialog,
+    doc: /* Non-nil means prompt with the old GTK file selection dialog.
+If nil or if the file selection dialog is not available, the new GTK file
+chooser is used instead.  To turn off all file dialogs set the
+variable `use-file-dialog'.  */);
+  x_gtk_use_old_file_dialog = false;
+
+  DEFVAR_BOOL ("x-gtk-show-hidden-files", x_gtk_show_hidden_files,
+    doc: /* If non-nil, the GTK file chooser will by default show hidden files.
+Note that this is just the default, there is a toggle button on the file
+chooser to show or not show hidden files on a case by case basis.  */);
+  x_gtk_show_hidden_files = false;
+
+  DEFVAR_BOOL ("x-gtk-file-dialog-help-text", x_gtk_file_dialog_help_text,
+    doc: /* If non-nil, the GTK file chooser will show additional help text.
+If more space for files in the file chooser dialog is wanted, set this to nil
+to turn the additional text off.  */);
+  x_gtk_file_dialog_help_text = true;
+
+  DEFVAR_BOOL ("x-gtk-use-system-tooltips", x_gtk_use_system_tooltips,
+    doc: /* If non-nil with a Gtk+ built Emacs, the Gtk+ tooltip is used.
+Otherwise use Emacs own tooltip implementation.
+When using Gtk+ tooltips, the tooltip face is not used.  */);
+  x_gtk_use_system_tooltips = true;
+
+
+  DEFSYM (Qmono, "mono");
+
+  DEFSYM (Qpdf, "pdf");
+
+  DEFSYM (Qorientation, "orientation");
+  DEFSYM (Qtop_margin, "top-margin");
+  DEFSYM (Qbottom_margin, "bottom-margin");
+  DEFSYM (Qportrait, "portrait");
+  DEFSYM (Qlandscape, "landscape");
+  DEFSYM (Qreverse_portrait, "reverse-portrait");
+  DEFSYM (Qreverse_landscape, "reverse-landscape");
+}
+
+#ifdef PGTK_DEBUG
+
+#include <stdarg.h>
+#include <time.h>
+void pgtk_log(const char *file, int lineno, const char *fmt, ...)
+{
+  struct timespec ts;
+  struct tm tm;
+  char timestr[32];
+  va_list ap;
+
+  clock_gettime(CLOCK_REALTIME, &ts);
+
+  localtime_r(&ts.tv_sec, &tm);
+  strftime(timestr, sizeof timestr, "%H:%M:%S", &tm);
+
+  fprintf(stderr, "%s.%06ld %.10s:%04d ", timestr, ts.tv_nsec / 1000, file, 
lineno);
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap);
+  va_end(ap);
+  fputc('\n', stderr);
+}
+
+void pgtk_backtrace(const char *file, int lineno)
+{
+  Lisp_Object bt = make_uninit_vector(10);
+  for (int i = 0; i < 10; i++)
+    ASET(bt, i, Qnil);
+
+  struct timespec ts;
+  struct tm tm;
+  char timestr[32];
+
+  clock_gettime(CLOCK_REALTIME, &ts);
+
+  localtime_r(&ts.tv_sec, &tm);
+  strftime(timestr, sizeof timestr, "%H:%M:%S", &tm);
+
+  fprintf(stderr, "%s.%06ld %.10s:%04d ********\n", timestr, ts.tv_nsec / 
1000, file, lineno);
+
+  get_backtrace(bt);
+  for (int i = 0; i < 10; i++) {
+    Lisp_Object stk = AREF(bt, i);
+    if (!NILP(stk)) {
+      Lisp_Object args[2] = { build_string("%S"), stk };
+      Lisp_Object str = Fformat(2, args);
+      fprintf(stderr, "%s %.10s:%04d %s\n", timestr, file, lineno, 
SSDATA(str));
+    }
+  }
+
+  fprintf(stderr, "%s %.10s:%04d ********\n", timestr, file, lineno);
+}
+
+#endif
+
+#endif
diff --git a/src/pgtkgui.h b/src/pgtkgui.h
new file mode 100644
index 0000000..78e1da7
--- /dev/null
+++ b/src/pgtkgui.h
@@ -0,0 +1,136 @@
+/* Definitions and headers for communication on the pure Gtk+3.
+   Copyright (C) 1995, 2005, 2008-2017 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef __PGTKGUI_H__
+#define __PGTKGUI_H__
+
+/* Emulate XCharStruct.  */
+typedef struct _XCharStruct
+{
+  int rbearing;
+  int lbearing;
+  int width;
+  int ascent;
+  int descent;
+} XCharStruct;
+
+/* Fake structure from Xlib.h to represent two-byte characters.  */
+typedef unsigned short unichar;
+typedef unichar XChar2b;
+
+#define STORE_XCHAR2B(chp, b1, b2) \
+  (*(chp) = ((XChar2b)((((b1) & 0x00ff) << 8) | ((b2) & 0x00ff))))
+
+#define XCHAR2B_BYTE1(chp) \
+  ((*(chp) & 0xff00) >> 8)
+
+#define XCHAR2B_BYTE2(chp) \
+  (*(chp) & 0x00ff)
+
+
+/* XXX: xfaces requires these structures, but the question is are we
+        forced to use them? */
+typedef struct _XGCValues
+{
+  unsigned long foreground;
+  unsigned long background;
+  void *font;
+} XGCValues;
+
+typedef XGCValues * GC;
+
+#define GCForeground 0x01
+#define GCBackground 0x02
+#define GCFont 0x03
+
+typedef void *Pixmap;
+
+typedef void *Cursor;
+
+#define No_Cursor (0)
+
+typedef void * Color;
+typedef int Window;
+typedef struct _GdkDisplay Display;
+
+/* Xism */
+typedef Lisp_Object XrmDatabase;
+
+
+/* some sort of attempt to normalize rectangle handling.. seems a bit much
+   for what is accomplished */
+typedef struct {
+      int x, y;
+      unsigned width, height;
+} XRectangle;
+
+#define NativeRectangle XRectangle
+
+#define CONVERT_TO_EMACS_RECT(xr, nr)          \
+  ((xr).x     = (nr).origin.x,                 \
+   (xr).y     = (nr).origin.y,                 \
+   (xr).width = (nr).size.width,               \
+   (xr).height = (nr).size.height)
+
+#define CONVERT_FROM_EMACS_RECT(xr, nr)        \
+  ((nr).x    = (xr).x,                 \
+   (nr).y    = (xr).y,                 \
+   (nr).width  = (xr).width,           \
+   (nr).height = (xr).height)
+
+#define STORE_NATIVE_RECT(nr, px, py, pwidth, pheight) \
+  ((nr).x    = (px),                   \
+   (nr).y    = (py),                   \
+   (nr).width  = (pwidth),             \
+   (nr).height = (pheight))
+
+/* This stuff needed by frame.c. */
+#define ForgetGravity          0
+#define NorthWestGravity       1
+#define NorthGravity           2
+#define NorthEastGravity       3
+#define WestGravity            4
+#define CenterGravity          5
+#define EastGravity            6
+#define SouthWestGravity       7
+#define SouthGravity           8
+#define SouthEastGravity       9
+#define StaticGravity          10
+
+#define NoValue                0x0000
+#define XValue         0x0001
+#define YValue         0x0002
+#define WidthValue     0x0004
+#define HeightValue    0x0008
+#define AllValues      0x000F
+#define XNegative      0x0010
+#define YNegative      0x0020
+
+#define USPosition     (1L << 0) /* user specified x, y */
+#define USSize         (1L << 1) /* user specified width, height */
+
+#define PPosition      (1L << 2) /* program specified position */
+#define PSize          (1L << 3) /* program specified size */
+#define PMinSize       (1L << 4) /* program specified minimum size */
+#define PMaxSize       (1L << 5) /* program specified maximum size */
+#define PResizeInc     (1L << 6) /* program specified resize increments */
+#define PAspect                (1L << 7) /* program specified min, max aspect 
ratios */
+#define PBaseSize      (1L << 8) /* program specified base for incrementing */
+#define PWinGravity    (1L << 9) /* program specified window gravity */
+
+#endif  /* __PGTKGUI_H__ */
diff --git a/src/pgtkselect.c b/src/pgtkselect.c
new file mode 100644
index 0000000..b885425
--- /dev/null
+++ b/src/pgtkselect.c
@@ -0,0 +1,466 @@
+/* Gtk selection processing for emacs.
+   Copyright (C) 1993-1994, 2005-2006, 2008-2017 Free Software
+   Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/*
+Originally by Carl Edman
+Updated by Christian Limpach (chris@nice.ch)
+OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com)
+macOS/Aqua port by Christophe de Dinechin (descubes@earthlink.net)
+GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
+*/
+
+/* This should be the first include, as it may set up #defines affecting
+   interpretation of even the system includes.  */
+#include <config.h>
+
+#include "lisp.h"
+#include "pgtkterm.h"
+#include "termhooks.h"
+#include "keyboard.h"
+#include "pgtkselect.h"
+#include <gdk/gdk.h>
+
+#if 0
+static Lisp_Object Vselection_alist;
+#endif
+
+static GQuark quark_primary_data = 0;
+static GQuark quark_primary_size = 0;
+static GQuark quark_secondary_data = 0;
+static GQuark quark_secondary_size = 0;
+static GQuark quark_clipboard_data = 0;
+static GQuark quark_clipboard_size = 0;
+
+/* ==========================================================================
+
+    Internal utility functions
+
+   ========================================================================== 
*/
+
+/* From a Lisp_Object, return a suitable frame for selection
+   operations.  OBJECT may be a frame, a terminal object, or nil
+   (which stands for the selected frame--or, if that is not an pgtk
+   frame, the first pgtk display on the list).  If no suitable frame can
+   be found, return NULL.  */
+
+static struct frame *
+frame_for_pgtk_selection (Lisp_Object object)
+{
+  Lisp_Object tail, frame;
+  struct frame *f;
+
+  if (NILP (object))
+    {
+      f = XFRAME (selected_frame);
+      if (FRAME_PGTK_P (f) && FRAME_LIVE_P (f))
+       return f;
+
+      FOR_EACH_FRAME (tail, frame)
+       {
+         f = XFRAME (frame);
+         if (FRAME_PGTK_P (f) && FRAME_LIVE_P (f))
+           return f;
+       }
+    }
+  else if (TERMINALP (object))
+    {
+      struct terminal *t = decode_live_terminal (object);
+
+      if (t->type == output_pgtk)
+       FOR_EACH_FRAME (tail, frame)
+         {
+           f = XFRAME (frame);
+           if (FRAME_LIVE_P (f) && f->terminal == t)
+             return f;
+         }
+    }
+  else if (FRAMEP (object))
+    {
+      f = XFRAME (object);
+      if (FRAME_PGTK_P (f) && FRAME_LIVE_P (f))
+       return f;
+    }
+
+  return NULL;
+}
+
+static GtkClipboard *symbol_to_gtk_clipboard(GtkWidget *widget, Lisp_Object 
symbol)
+{
+  GdkAtom atom;
+
+  CHECK_SYMBOL (symbol);
+  if (NILP(symbol)) {
+    atom = GDK_SELECTION_PRIMARY;
+  } else if (EQ(symbol, QCLIPBOARD)) {
+    atom = GDK_SELECTION_CLIPBOARD;
+  } else if (EQ(symbol, QPRIMARY)) {
+    atom = GDK_SELECTION_PRIMARY;
+  } else if (EQ(symbol, QSECONDARY)) {
+    atom = GDK_SELECTION_SECONDARY;
+  } else if (EQ(symbol, Qt)) {
+    atom = GDK_SELECTION_SECONDARY;
+  } else {
+    atom = 0;
+    error ("Bad selection");
+  }
+
+  return gtk_widget_get_clipboard(widget, atom);
+}
+
+static void selection_type_to_quarks(GdkAtom type, GQuark *quark_data, GQuark 
*quark_size)
+{
+  if (type == GDK_SELECTION_PRIMARY) {
+    *quark_data = quark_primary_data;
+    *quark_size = quark_primary_size;
+  } else if (type == GDK_SELECTION_SECONDARY) {
+    *quark_data = quark_secondary_data;
+    *quark_size = quark_secondary_size;
+  } else if (type == GDK_SELECTION_CLIPBOARD) {
+    *quark_data = quark_clipboard_data;
+    *quark_size = quark_clipboard_size;
+  } else {
+    /* fixme: Is it safe to use 'error' here? */
+    error("Unknown selection type.");
+  }
+}
+
+static void
+get_func(GtkClipboard *cb, GtkSelectionData *data, guint info, gpointer 
user_data_or_owner)
+{
+  PGTK_TRACE("get_func:");
+  GObject *obj = G_OBJECT(user_data_or_owner);
+  const char *str;
+  int size;
+  GQuark quark_data, quark_size;
+
+  selection_type_to_quarks(gtk_clipboard_get_selection(cb), &quark_data, 
&quark_size);
+
+  str = g_object_get_qdata(obj, quark_data);
+  size = GPOINTER_TO_SIZE(g_object_get_qdata(obj, quark_size));
+  PGTK_TRACE("get_func: str: %s", str);
+  gtk_selection_data_set_text(data, str, size);
+}
+
+static void
+clear_func(GtkClipboard *cb, gpointer user_data_or_owner)
+{
+  PGTK_TRACE("clear_func:");
+  GObject *obj = G_OBJECT(user_data_or_owner);
+  GQuark quark_data, quark_size;
+
+  selection_type_to_quarks(gtk_clipboard_get_selection(cb), &quark_data, 
&quark_size);
+
+  g_object_set_qdata(obj, quark_data, NULL);
+  g_object_set_qdata(obj, quark_size, 0);
+}
+
+
+/* ==========================================================================
+
+    Functions used externally
+
+   ========================================================================== 
*/
+
+void pgtk_selection_init(void)
+{
+  if (quark_primary_data == 0) {
+    quark_primary_data = g_quark_from_static_string("pgtk-primary-data");
+    quark_primary_size = g_quark_from_static_string("pgtk-primary-size");
+    quark_secondary_data = g_quark_from_static_string("pgtk-secondary-data");
+    quark_secondary_size = g_quark_from_static_string("pgtk-secondary-size");
+    quark_clipboard_data = g_quark_from_static_string("pgtk-clipboard-data");
+    quark_clipboard_size = g_quark_from_static_string("pgtk-clipboard-size");
+  }
+}
+
+void pgtk_selection_lost(GtkWidget *widget, GdkEventSelection *event, gpointer 
user_data)
+{
+  GQuark quark_data, quark_size;
+  PGTK_TRACE("pgtk_selection_lost:");
+
+  selection_type_to_quarks(event->selection, &quark_data, &quark_size);
+
+  g_object_set_qdata(G_OBJECT(widget), quark_data, NULL);
+  g_object_set_qdata(G_OBJECT(widget), quark_size, 0);
+}
+
+/* ==========================================================================
+
+    Lisp Defuns
+
+   ========================================================================== 
*/
+
+
+DEFUN ("pgtk-own-selection-internal", Fpgtk_own_selection_internal,
+       Spgtk_own_selection_internal, 2, 3, 0,
+       doc: /* Assert an X selection of type SELECTION and value VALUE.
+SELECTION is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
+\(Those are literal upper-case symbol names, since that's what X expects.)
+VALUE is typically a string, or a cons of two markers, but may be
+anything that the functions on `selection-converter-alist' know about.
+
+FRAME should be a frame that should own the selection.  If omitted or
+nil, it defaults to the selected frame.*/)
+     (Lisp_Object selection, Lisp_Object value, Lisp_Object frame)
+{
+  PGTK_TRACE("pgtk-own-selection-internal.");
+  Lisp_Object successful_p = Qnil;
+  Lisp_Object target_symbol, rest;
+  GtkClipboard *cb;
+  struct frame *f;
+  GQuark quark_data, quark_size;
+
+  check_window_system (NULL);
+
+  if (NILP (frame)) frame = selected_frame;
+  if (!FRAME_LIVE_P (XFRAME (frame)) || !FRAME_PGTK_P (XFRAME (frame)))
+    error ("pgtk selection unavailable for this frame");
+  f = XFRAME(frame);
+
+  cb = symbol_to_gtk_clipboard(FRAME_GTK_WIDGET(f), selection);
+  selection_type_to_quarks(gtk_clipboard_get_selection(cb), &quark_data, 
&quark_size);
+
+  /* We only support copy of text.  */
+  target_symbol = QTEXT;
+  if (STRINGP (value))
+    {
+      GtkTargetList *list;
+      GtkTargetEntry *targets;
+      gint n_targets;
+      GtkWidget *widget;
+
+      list = gtk_target_list_new (NULL, 0);
+      gtk_target_list_add_text_targets (list, 0);
+
+      targets = gtk_target_table_new_from_list (list, &n_targets);
+
+      int size = SBYTES(value);
+      gchar *str = xmalloc(size + 1);
+      memcpy(str, SSDATA(value), size);
+      str[size] = '\0';
+
+      widget = FRAME_GTK_WIDGET(f);
+      g_object_set_qdata_full(G_OBJECT(widget), quark_data, str, xfree);
+      g_object_set_qdata_full(G_OBJECT(widget), quark_size, 
GSIZE_TO_POINTER(size), NULL);
+
+      PGTK_TRACE("set_with_owner: owner=%p", FRAME_GTK_WIDGET(f));
+      if (gtk_clipboard_set_with_owner (cb,
+                                       targets, n_targets,
+                                       get_func, clear_func,
+                                       G_OBJECT(FRAME_GTK_WIDGET(f)))) {
+       PGTK_TRACE("set_with_owner succeeded..");
+       successful_p = Qt;
+      } else {
+       PGTK_TRACE("set_with_owner failed.");
+      }
+      gtk_clipboard_set_can_store (cb, NULL, 0);
+
+      gtk_target_table_free (targets, n_targets);
+      gtk_target_list_unref (list);
+    }
+
+  if (!EQ (Vpgtk_sent_selection_hooks, Qunbound))
+    {
+      /* FIXME: Use run-hook-with-args!  */
+      for (rest = Vpgtk_sent_selection_hooks; CONSP (rest); rest = Fcdr (rest))
+        call3 (Fcar (rest), selection, target_symbol, successful_p);
+    }
+
+  return value;
+}
+
+
+DEFUN ("pgtk-disown-selection-internal", Fpgtk_disown_selection_internal,
+       Spgtk_disown_selection_internal, 1, 3, 0,
+       doc: /* If we own the selection SELECTION, disown it.
+Disowning it means there is no such selection.
+
+Sets the last-change time for the selection to TIME-OBJECT (by default
+the time of the last event).
+
+TERMINAL should be a terminal object or a frame specifying the X
+server to query.  If omitted or nil, that stands for the selected
+frame's display, or the first available X display.
+
+On Nextstep, the TIME-OBJECT and TERMINAL arguments are unused.
+On MS-DOS, all this does is return non-nil if we own the selection.
+On PGTK, the TIME-OBJECT is unused.  */)
+  (Lisp_Object selection, Lisp_Object time_object, Lisp_Object terminal)
+{
+  PGTK_TRACE("pgtk-disown-selection-internal.");
+
+  struct frame *f = frame_for_pgtk_selection (terminal);
+  GtkClipboard *cb;
+
+  if (!f)
+    return Qnil;
+
+  cb = symbol_to_gtk_clipboard(FRAME_GTK_WIDGET(f), selection);
+
+  gtk_clipboard_clear(cb);
+
+  return Qt;
+}
+
+
+DEFUN ("pgtk-selection-exists-p", Fpgtk_selection_exists_p, 
Spgtk_selection_exists_p,
+       0, 2, 0, doc: /* Whether there is an owner for the given X selection.
+SELECTION should be the name of the selection in question, typically
+one of the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.  (X expects
+these literal upper-case names.)  The symbol nil is the same as
+`PRIMARY', and t is the same as `SECONDARY'.
+
+TERMINAL should be a terminal object or a frame specifying the X
+server to query.  If omitted or nil, that stands for the selected
+frame's display, or the first available X display.
+
+On Nextstep, TERMINAL is unused.  */)
+     (Lisp_Object selection, Lisp_Object terminal)
+{
+  PGTK_TRACE("pgtk-selection-exists-p.");
+  struct frame *f = frame_for_pgtk_selection (terminal);
+  GtkClipboard *cb;
+
+  if (!f)
+    return Qnil;
+
+  cb = symbol_to_gtk_clipboard(FRAME_GTK_WIDGET(f), selection);
+
+  return gtk_clipboard_wait_is_text_available(cb) ? Qt : Qnil;
+}
+
+
+DEFUN ("pgtk-selection-owner-p", Fpgtk_selection_owner_p, 
Spgtk_selection_owner_p,
+       0, 2, 0,
+       doc: /* Whether the current Emacs process owns the given X Selection.
+The arg should be the name of the selection in question, typically one of
+the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
+\(Those are literal upper-case symbol names, since that's what X expects.)
+For convenience, the symbol nil is the same as `PRIMARY',
+and t is the same as `SECONDARY'.
+
+TERMINAL should be a terminal object or a frame specifying the X
+server to query.  If omitted or nil, that stands for the selected
+frame's display, or the first available X display.
+
+On Nextstep, TERMINAL is unused.  */)
+     (Lisp_Object selection, Lisp_Object terminal)
+{
+  PGTK_TRACE("pgtk-selection-owner-p.");
+  struct frame *f = frame_for_pgtk_selection (terminal);
+  GtkClipboard *cb;
+  GObject *obj;
+  GQuark quark_data, quark_size;
+
+  cb = symbol_to_gtk_clipboard(FRAME_GTK_WIDGET(f), selection);
+  selection_type_to_quarks(gtk_clipboard_get_selection(cb), &quark_data, 
&quark_size);
+
+  obj = gtk_clipboard_get_owner(cb);
+
+  return g_object_get_qdata(obj, quark_data) != NULL ? Qt : Qnil;
+}
+
+
+DEFUN ("pgtk-get-selection-internal", Fpgtk_get_selection_internal,
+       Spgtk_get_selection_internal, 2, 4, 0,
+       doc: /* Return text selected from some X window.
+SELECTION-SYMBOL is typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
+\(Those are literal upper-case symbol names, since that's what X expects.)
+TARGET-TYPE is the type of data desired, typically `STRING'.
+
+TIME-STAMP is the time to use in the XConvertSelection call for foreign
+selections.  If omitted, defaults to the time for the last event.
+
+TERMINAL should be a terminal object or a frame specifying the X
+server to query.  If omitted or nil, that stands for the selected
+frame's display, or the first available X display.
+
+On Nextstep, TIME-STAMP and TERMINAL are unused.
+On PGTK, TIME-STAMP is unused.  */)
+  (Lisp_Object selection_symbol, Lisp_Object target_type,
+   Lisp_Object time_stamp, Lisp_Object terminal)
+{
+  struct frame *f = frame_for_pgtk_selection (terminal);
+  GtkClipboard *cb;
+
+  CHECK_SYMBOL (selection_symbol);
+  CHECK_SYMBOL (target_type);
+  if (EQ (target_type, QMULTIPLE))
+    error ("Retrieving MULTIPLE selections is currently unimplemented");
+  if (!f)
+    error ("PGTK selection unavailable for this frame");
+
+  cb = symbol_to_gtk_clipboard(FRAME_GTK_WIDGET(f), selection_symbol);
+
+  gchar *s = gtk_clipboard_wait_for_text(cb);
+  if (s == NULL)
+    return Qnil;
+  int size = strlen(s);
+  Lisp_Object str = make_unibyte_string (s, size);
+  Fput_text_property (make_fixnum (0), make_fixnum (size),
+                     Qforeign_selection, QUTF8_STRING, str);
+  return str;
+}
+
+
+void
+nxatoms_of_pgtkselect (void)
+{
+  PGTK_TRACE("nxatoms_of_pgtkselect");
+}
+
+void
+syms_of_pgtkselect (void)
+{
+  PGTK_TRACE("syms_of_pgtkselect");
+
+  DEFSYM (QCLIPBOARD, "CLIPBOARD");
+  DEFSYM (QSECONDARY, "SECONDARY");
+  DEFSYM (QTEXT, "TEXT");
+  DEFSYM (QFILE_NAME, "FILE_NAME");
+  DEFSYM (QMULTIPLE, "MULTIPLE");
+
+  DEFSYM (Qforeign_selection, "foreign-selection");
+  DEFSYM (QUTF8_STRING, "UTF8_STRING");
+
+  defsubr (&Spgtk_disown_selection_internal);
+  defsubr (&Spgtk_get_selection_internal);
+  defsubr (&Spgtk_own_selection_internal);
+  defsubr (&Spgtk_selection_exists_p);
+  defsubr (&Spgtk_selection_owner_p);
+
+#if 0
+  Vselection_alist = Qnil;
+  staticpro (&Vselection_alist);
+#endif
+
+  DEFVAR_LISP ("pgtk-sent-selection-hooks", Vpgtk_sent_selection_hooks,
+               "A list of functions to be called when Emacs answers a 
selection request.\n\
+The functions are called with four arguments:\n\
+  - the selection name (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\
+  - the selection-type which Emacs was asked to convert the\n\
+    selection into before sending (for example, `STRING' or `LENGTH');\n\
+  - a flag indicating success or failure for responding to the request.\n\
+We might have failed (and declined the request) for any number of reasons,\n\
+including being asked for a selection that we no longer own, or being asked\n\
+to convert into a type that we don't know about or that is inappropriate.\n\
+This hook doesn't let you change the behavior of Emacs's selection replies,\n\
+it merely informs you that they have happened.");
+  Vpgtk_sent_selection_hooks = Qnil;
+}
diff --git a/src/xsettings.h b/src/pgtkselect.h
similarity index 57%
copy from src/xsettings.h
copy to src/pgtkselect.h
index f29ce77..a67b221 100644
--- a/src/xsettings.h
+++ b/src/pgtkselect.h
@@ -1,6 +1,6 @@
-/* Functions for handle font changes dynamically.
-
-Copyright (C) 2009-2020 Free Software Foundation, Inc.
+/* Definitions and headers for selection of pure Gtk+3.
+   Copyright (C) 1989, 1993, 2005, 2008-2017 Free Software Foundation,
+   Inc.
 
 This file is part of GNU Emacs.
 
@@ -17,19 +17,15 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 
-#ifndef XSETTINGS_H
-#define XSETTINGS_H
 
-#include <X11/Xlib.h>
+#include "dispextern.h"
+#include "frame.h"
 
-struct x_display_info;
+#ifdef HAVE_PGTK
 
-extern void xsettings_initialize (struct x_display_info *);
-extern void xft_settings_event (struct x_display_info *, const XEvent *);
-extern const char *xsettings_get_system_font (void);
-#ifdef USE_LUCID
-extern const char *xsettings_get_system_normal_font (void);
-#endif
+#include <gtk/gtk.h>
 
+extern void pgtk_selection_init(void);
+void pgtk_selection_lost(GtkWidget *widget, GdkEventSelection *event, gpointer 
user_data);
 
-#endif /* XSETTINGS_H */
+#endif /* HAVE_PGTK */
diff --git a/src/pgtkterm.c b/src/pgtkterm.c
new file mode 100644
index 0000000..61da7d6
--- /dev/null
+++ b/src/pgtkterm.c
@@ -0,0 +1,6395 @@
+/* Pure Gtk+-3 communication module.      -*- coding: utf-8 -*-
+
+Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2017 Free Software
+Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* This should be the first include, as it may set up #defines affecting
+   interpretation of even the system includes. */
+#include <config.h>
+
+#include <fcntl.h>
+#include <math.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <time.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <c-ctype.h>
+#include <c-strcase.h>
+#include <ftoastr.h>
+
+#include "lisp.h"
+#include "blockinput.h"
+#include "sysselect.h"
+#include "gtkutil.h"
+#include "systime.h"
+#include "character.h"
+#include "xwidget.h"
+#include "fontset.h"
+#include "composite.h"
+#include "ccl.h"
+#include "dynlib.h"
+
+#include "termhooks.h"
+#include "termopts.h"
+#include "termchar.h"
+#include "menu.h"
+#include "window.h"
+#include "keyboard.h"
+#include "atimer.h"
+#include "buffer.h"
+#include "font.h"
+#include "xsettings.h"
+#include "pgtkselect.h"
+
+#define STORE_KEYSYM_FOR_DEBUG(keysym) ((void)0)
+
+#define FRAME_CR_CONTEXT(f)    ((f)->output_data.pgtk->cr_context)
+#define FRAME_CR_SURFACE(f)    ((f)->output_data.pgtk->cr_surface)
+
+struct pgtk_display_info *x_display_list; /* Chain of existing displays */
+extern Lisp_Object tip_frame;
+
+static struct event_queue_t {
+  union buffered_input_event *q;
+  int nr, cap;
+} event_q = {
+  NULL, 0, 0,
+};
+
+/* Non-zero timeout value means ignore next mouse click if it arrives
+   before that timeout elapses (i.e. as part of the same sequence of
+   events resulting from clicking on a frame to select it).  */
+
+static Time ignore_next_mouse_click_timeout;
+
+static void pgtk_delete_display (struct pgtk_display_info *dpyinfo);
+static void pgtk_clear_frame_area(struct frame *f, int x, int y, int width, 
int height);
+static void pgtk_fill_rectangle(struct frame *f, unsigned long color, int x, 
int y, int width, int height);
+static void pgtk_clip_to_row (struct window *w, struct glyph_row *row,
+                               enum glyph_row_area area, cairo_t *cr);
+static struct frame *
+pgtk_any_window_to_frame (GdkWindow *window);
+
+
+static void evq_enqueue(union buffered_input_event *ev)
+{
+  struct event_queue_t *evq = &event_q;
+  if (evq->cap == 0) {
+    evq->cap = 4;
+    evq->q = xmalloc(sizeof *evq->q * evq->cap);
+  }
+
+  if (evq->nr >= evq->cap) {
+    evq->cap += evq->cap / 2;
+    evq->q = xrealloc(evq->q, sizeof *evq->q * evq->cap);
+  }
+
+  evq->q[evq->nr++] = *ev;
+  raise(SIGIO);
+}
+
+static int evq_flush(struct input_event *hold_quit)
+{
+  struct event_queue_t *evq = &event_q;
+  int i, n = evq->nr;
+  for (i = 0; i < n; i++)
+    kbd_buffer_store_buffered_event (&evq->q[i], hold_quit);
+  evq->nr = 0;
+  return n;
+}
+
+void
+mark_pgtkterm(void)
+{
+  struct event_queue_t *evq = &event_q;
+  int i, n = evq->nr;
+  for (i = 0; i < n; i++) {
+    union buffered_input_event *ev = &evq->q[i];
+    mark_object (ev->ie.x);
+    mark_object (ev->ie.y);
+    mark_object (ev->ie.frame_or_window);
+    mark_object (ev->ie.arg);
+  }
+
+  struct pgtk_display_info *dpyinfo;
+  for (dpyinfo = x_display_list; dpyinfo != NULL; dpyinfo = dpyinfo->next) {
+    mark_object (dpyinfo->name_list_element);
+    mark_object (dpyinfo->xrdb);
+  }
+}
+
+char *
+x_get_keysym_name (int keysym)
+/* --------------------------------------------------------------------------
+    Called by keyboard.c.  Not sure if the return val is important, except
+    that it be unique.
+   -------------------------------------------------------------------------- 
*/
+{
+  PGTK_TRACE("x_get_ksysym_name");
+  static char value[16];
+  sprintf (value, "%d", keysym);
+  return value;
+}
+
+void
+frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
+/* --------------------------------------------------------------------------
+     Programmatically reposition mouse pointer in pixel coordinates
+   -------------------------------------------------------------------------- 
*/
+{
+  PGTK_TRACE("frame_set_mouse_pixel_position");
+}
+
+/* Free X resources of frame F.  */
+
+void
+x_free_frame_resources (struct frame *f)
+{
+  struct pgtk_display_info *dpyinfo;
+  Mouse_HLInfo *hlinfo;
+
+  PGTK_TRACE ("x_free_frame_resources");
+  check_window_system (f);
+  dpyinfo = FRAME_DISPLAY_INFO (f);
+  hlinfo = MOUSE_HL_INFO (f);
+
+  block_input ();
+
+  free_frame_faces (f);
+
+#define CLEAR_IF_EQ(FIELD)     \
+  do { if (f == dpyinfo->FIELD) dpyinfo->FIELD = 0; } while (false)
+
+  CLEAR_IF_EQ(x_focus_frame);
+  CLEAR_IF_EQ(x_highlight_frame);
+  CLEAR_IF_EQ(x_focus_event_frame);
+  CLEAR_IF_EQ(last_mouse_frame);
+  CLEAR_IF_EQ(last_mouse_motion_frame);
+  CLEAR_IF_EQ(last_mouse_glyph_frame);
+
+#undef CLEAR_IF_EQ
+
+  if (f == hlinfo->mouse_face_mouse_frame)
+    reset_mouse_highlight (hlinfo);
+
+  gtk_widget_destroy(FRAME_GTK_OUTER_WIDGET(f));
+
+  if (FRAME_X_OUTPUT(f)->cr_surface_visible_bell != NULL) {
+    cairo_surface_destroy(FRAME_X_OUTPUT(f)->cr_surface_visible_bell);
+    FRAME_X_OUTPUT(f)->cr_surface_visible_bell = NULL;
+  }
+
+  if (FRAME_X_OUTPUT(f)->atimer_visible_bell != NULL) {
+    cancel_atimer(FRAME_X_OUTPUT(f)->atimer_visible_bell);
+    FRAME_X_OUTPUT(f)->atimer_visible_bell = NULL;
+  }
+
+  xfree (f->output_data.pgtk);
+  f->output_data.pgtk = NULL;
+
+  unblock_input ();
+}
+
+void
+x_destroy_window (struct frame *f)
+/* --------------------------------------------------------------------------
+     External: Delete the window
+   -------------------------------------------------------------------------- 
*/
+{
+  struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+  PGTK_TRACE ("x_destroy_window");
+
+  check_window_system (f);
+  if (dpyinfo->gdpy != NULL)
+    x_free_frame_resources (f);
+
+  dpyinfo->reference_count--;
+}
+
+/* Calculate the absolute position in frame F
+   from its current recorded position values and gravity.  */
+
+static void
+x_calc_absolute_position (struct frame *f)
+{
+  int flags = f->size_hint_flags;
+  struct frame *p = FRAME_PARENT_FRAME (f);
+
+  /* We have nothing to do if the current position
+     is already for the top-left corner.  */
+  if (! ((flags & XNegative) || (flags & YNegative)))
+    return;
+
+  /* Treat negative positions as relative to the leftmost bottommost
+     position that fits on the screen.  */
+  if ((flags & XNegative) && (f->left_pos <= 0))
+    {
+      int width = FRAME_PIXEL_WIDTH (f);
+
+      /* A frame that has been visible at least once should have outer
+        edges.  */
+      if (FRAME_X_OUTPUT(f)->has_been_visible && !p)
+       {
+         Lisp_Object frame;
+         Lisp_Object edges = Qnil;
+
+         XSETFRAME (frame, f);
+         edges = Fpgtk_frame_edges (frame, Qouter_edges);
+         if (!NILP (edges))
+           width = (XFIXNUM (Fnth (make_fixnum (2), edges))
+                    - XFIXNUM (Fnth (make_fixnum (0), edges)));
+       }
+
+      if (p)
+       f->left_pos = (FRAME_PIXEL_WIDTH (p) - width - 2 * f->border_width
+                      + f->left_pos);
+      else
+       f->left_pos = (x_display_pixel_width (FRAME_DISPLAY_INFO (f))
+                      - width + f->left_pos);
+
+    }
+
+  if ((flags & YNegative) && (f->top_pos <= 0))
+    {
+      int height = FRAME_PIXEL_HEIGHT (f);
+
+      if (FRAME_X_OUTPUT(f)->has_been_visible && !p)
+       {
+         Lisp_Object frame;
+         Lisp_Object edges = Qnil;
+
+         XSETFRAME (frame, f);
+         if (NILP (edges))
+           edges = Fpgtk_frame_edges (frame, Qouter_edges);
+         if (!NILP (edges))
+           height = (XFIXNUM (Fnth (make_fixnum (3), edges))
+                     - XFIXNUM (Fnth (make_fixnum (1), edges)));
+       }
+
+      if (p)
+       f->top_pos = (FRAME_PIXEL_HEIGHT (p) - height - 2 * f->border_width
+                      + f->top_pos);
+      else
+       f->top_pos = (x_display_pixel_height (FRAME_DISPLAY_INFO (f))
+                     - height + f->top_pos);
+  }
+
+  /* The left_pos and top_pos
+     are now relative to the top and left screen edges,
+     so the flags should correspond.  */
+  f->size_hint_flags &= ~ (XNegative | YNegative);
+}
+
+/* CHANGE_GRAVITY is 1 when calling from Fset_frame_position,
+   to really change the position, and 0 when calling from
+   x_make_frame_visible (in that case, XOFF and YOFF are the current
+   position values).  It is -1 when calling from x_set_frame_parameters,
+   which means, do adjust for borders but don't change the gravity.  */
+
+static void
+x_set_offset (struct frame *f, int xoff, int yoff, int change_gravity)
+/* --------------------------------------------------------------------------
+     External: Position the window
+   -------------------------------------------------------------------------- 
*/
+{
+  /* not working on wayland. */
+
+  PGTK_TRACE("x_set_offset: %d,%d,%d.", xoff, yoff, change_gravity);
+
+  if (change_gravity > 0)
+    {
+      PGTK_TRACE("x_set_offset: change_gravity > 0");
+      f->top_pos = yoff;
+      f->left_pos = xoff;
+      f->size_hint_flags &= ~ (XNegative | YNegative);
+      if (xoff < 0)
+       f->size_hint_flags |= XNegative;
+      if (yoff < 0)
+       f->size_hint_flags |= YNegative;
+      f->win_gravity = NorthWestGravity;
+    }
+
+  x_calc_absolute_position (f);
+
+  block_input ();
+  x_wm_set_size_hint (f, 0, false);
+
+  /* When a position change was requested and the outer GTK widget
+     has been realized already, leave it to gtk_window_move to DTRT
+     and return.  Used for Bug#25851 and Bug#25943.  */
+  if (change_gravity != 0 && FRAME_GTK_OUTER_WIDGET (f)) {
+    PGTK_TRACE("x_set_offset: move to %d,%d.", f->left_pos, f->top_pos);
+    gtk_window_move (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                    f->left_pos, f->top_pos);
+  }
+
+  unblock_input ();
+}
+
+static void
+pgtk_set_window_size (struct frame *f,
+                   bool change_gravity,
+                   int width,
+                   int height,
+                   bool pixelwise)
+/* --------------------------------------------------------------------------
+     Adjust window pixel size based on given character grid size
+     Impl is a bit more complex than other terms, need to do some
+     internal clipping.
+   -------------------------------------------------------------------------- 
*/
+{
+  PGTK_TRACE("pgtk_set_window_size(%dx%d, %s)", width, height, pixelwise ? 
"pixel" : "char");
+  int pixelwidth, pixelheight;
+
+  block_input ();
+
+  gtk_widget_get_size_request(FRAME_GTK_WIDGET(f), &pixelwidth, &pixelheight);
+  PGTK_TRACE("old: %dx%d", pixelwidth, pixelheight);
+
+  if (pixelwise)
+    {
+      pixelwidth = FRAME_TEXT_TO_PIXEL_WIDTH (f, width);
+      pixelheight = FRAME_TEXT_TO_PIXEL_HEIGHT (f, height);
+    }
+  else
+    {
+      pixelwidth =  FRAME_TEXT_COLS_TO_PIXEL_WIDTH   (f, width);
+      pixelheight = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, height);
+    }
+
+  frame_size_history_add
+    (f, Qx_set_window_size_1, width, height,
+     list5 (Fcons (make_fixnum (pixelwidth), make_fixnum (pixelheight)),
+           Fcons (make_fixnum (pixelwidth), make_fixnum (pixelheight)),
+           make_fixnum (f->border_width),
+           make_fixnum (FRAME_PGTK_TITLEBAR_HEIGHT (f)),
+           make_fixnum (FRAME_TOOLBAR_HEIGHT (f))));
+
+  PGTK_TRACE("new: %dx%d", pixelwidth, pixelheight);
+  for (GtkWidget *w = FRAME_GTK_WIDGET(f); w != NULL; w = 
gtk_widget_get_parent(w)) {
+    PGTK_TRACE("%p %s %d %d", w, G_OBJECT_TYPE_NAME(w), 
gtk_widget_get_mapped(w), gtk_widget_get_visible(w));
+    gint wd, hi;
+    gtk_widget_get_size_request(w, &wd, &hi);
+    PGTK_TRACE(" %dx%d", wd, hi);
+    GtkAllocation alloc;
+    gtk_widget_get_allocation(w, &alloc);
+    PGTK_TRACE(" %dx%d+%d+%d", alloc.width, alloc.height, alloc.x, alloc.y);
+  }
+
+  PGTK_TRACE("pgtk_set_window_size: %p: %dx%d.", f, width, height);
+  f->output_data.pgtk->preferred_width = pixelwidth;
+  f->output_data.pgtk->preferred_height = pixelheight;
+  x_wm_set_size_hint(f, 0, 0);
+  xg_frame_set_char_size (f, FRAME_PIXEL_TO_TEXT_WIDTH(f, pixelwidth), 
FRAME_PIXEL_TO_TEXT_HEIGHT(f, pixelheight));
+  gtk_widget_queue_resize (FRAME_GTK_OUTER_WIDGET (f));
+
+  unblock_input ();
+}
+
+void
+x_iconify_frame (struct frame *f)
+/* --------------------------------------------------------------------------
+     External: Iconify window
+   -------------------------------------------------------------------------- 
*/
+{
+  PGTK_TRACE("x_iconify_frame");
+
+  /* Don't keep the highlight on an invisible frame.  */
+  if (FRAME_DISPLAY_INFO (f)->x_highlight_frame == f)
+    FRAME_DISPLAY_INFO (f)->x_highlight_frame = 0;
+
+  if (FRAME_ICONIFIED_P (f))
+    return;
+
+  block_input ();
+
+#if 0
+  x_set_bitmap_icon (f);
+#endif
+
+  if (FRAME_GTK_OUTER_WIDGET (f))
+    {
+      if (! FRAME_VISIBLE_P (f))
+        gtk_widget_show_all (FRAME_GTK_OUTER_WIDGET (f));
+
+      gtk_window_iconify (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
+      SET_FRAME_VISIBLE (f, 0);
+      SET_FRAME_ICONIFIED (f, true);
+      unblock_input ();
+      return;
+    }
+
+  /* Make sure the X server knows where the window should be positioned,
+     in case the user deiconifies with the window manager.  */
+  if (! FRAME_VISIBLE_P (f)
+      && ! FRAME_ICONIFIED_P (f)
+#if 0
+      && ! FRAME_X_EMBEDDED_P (f)
+#endif
+      )
+    x_set_offset (f, f->left_pos, f->top_pos, 0);
+
+#if 0
+  if (!FRAME_VISIBLE_P (f))
+    {
+      /* If the frame was withdrawn, before, we must map it.  */
+      XMapRaised (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
+    }
+#endif
+
+  SET_FRAME_ICONIFIED (f, true);
+  SET_FRAME_VISIBLE (f, 0);
+
+#if 0
+  XFlush (FRAME_X_DISPLAY (f));
+#else
+  gdk_flush();
+#endif
+  unblock_input ();
+}
+
+void
+x_make_frame_visible (struct frame *f)
+/* --------------------------------------------------------------------------
+     External: Show the window (X11 semantics)
+   -------------------------------------------------------------------------- 
*/
+{
+  PGTK_TRACE("x_make_frame_visible");
+#if 0
+  NSTRACE ("x_make_frame_visible");
+  /* XXX: at some points in past this was not needed, as the only place that
+     called this (frame.c:Fraise_frame ()) also called raise_lower;
+     if this ends up the case again, comment this out again. */
+  if (!FRAME_VISIBLE_P (f))
+    {
+      EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f);
+      NSWindow *window = [view window];
+
+      SET_FRAME_VISIBLE (f, 1);
+      ns_raise_frame (f, ! FRAME_NO_FOCUS_ON_MAP (f));
+
+      /* Making a new frame from a fullscreen frame will make the new frame
+         fullscreen also.  So skip handleFS as this will print an error.  */
+      if ([view fsIsNative] && f->want_fullscreen == FULLSCREEN_BOTH
+          && [view isFullscreen])
+        return;
+
+      if (f->want_fullscreen != FULLSCREEN_NONE)
+        {
+          block_input ();
+          [view handleFS];
+          unblock_input ();
+        }
+
+      /* Making a frame invisible seems to break the parent->child
+         relationship, so reinstate it. */
+      if ([window parentWindow] == nil && FRAME_PARENT_FRAME (f) != NULL)
+        {
+          NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window];
+
+          block_input ();
+          [parent addChildWindow: window
+                         ordered: NSWindowAbove];
+          unblock_input ();
+
+          /* If the parent frame moved while the child frame was
+             invisible, the child frame's position won't have been
+             updated.  Make sure it's in the right place now. */
+          x_set_offset(f, f->left_pos, f->top_pos, 0);
+        }
+    }
+#endif
+}
+
+
+void
+x_make_frame_invisible (struct frame *f)
+/* --------------------------------------------------------------------------
+     External: Hide the window (X11 semantics)
+   -------------------------------------------------------------------------- 
*/
+{
+  PGTK_TRACE("x_make_frame_invisible");
+#if 0
+  NSView *view;
+  NSTRACE ("x_make_frame_invisible");
+  check_window_system (f);
+  view = FRAME_NS_VIEW (f);
+  [[view window] orderOut: NSApp];
+  SET_FRAME_VISIBLE (f, 0);
+  SET_FRAME_ICONIFIED (f, 0);
+#endif
+}
+
+static Lisp_Object
+x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
+{
+  PGTK_TRACE("x_new_font");
+  struct font *font = XFONT_OBJECT (font_object);
+  int font_ascent, font_descent;
+
+  if (fontset < 0)
+    fontset = fontset_from_font (font_object);
+  FRAME_FONTSET (f) = fontset;
+
+  if (FRAME_FONT (f) == font) {
+    /* This font is already set in frame F.  There's nothing more to
+       do.  */
+    PGTK_TRACE("already set.");
+    return font_object;
+  }
+
+  FRAME_FONT (f) = font;
+  PGTK_TRACE("font:");
+  PGTK_TRACE("  %p", font);
+  PGTK_TRACE("  name: %s", SSDATA(font_get_name(font_object)));
+  PGTK_TRACE("  width: %d..%d", font->min_width, font->max_width);
+  PGTK_TRACE("  pixel_size: %d", font->pixel_size);
+  PGTK_TRACE("  height: %d", font->height);
+  PGTK_TRACE("  space_width: %d", font->space_width);
+  PGTK_TRACE("  average_width: %d", font->average_width);
+  PGTK_TRACE("  asc/desc: %d,%d", font->ascent, font->descent);
+  PGTK_TRACE("  ul thickness: %d", font->underline_thickness);
+  PGTK_TRACE("  ul position: %d", font->underline_position);
+  PGTK_TRACE("  vertical_centering: %d", font->vertical_centering);
+  PGTK_TRACE("  baseline_offset: %d", font->baseline_offset);
+  PGTK_TRACE("  relative_compose: %d", font->relative_compose);
+  PGTK_TRACE("  default_ascent: %d", font->default_ascent);
+  PGTK_TRACE("  encoding_charset: %d", font->encoding_charset);
+  PGTK_TRACE("  repertory_charset: %d", font->repertory_charset);
+
+  FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
+  FRAME_COLUMN_WIDTH (f) = font->average_width;
+  get_font_ascent_descent (font, &font_ascent, &font_descent);
+  FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
+
+  /* Compute the scroll bar width in character columns.  */
+  if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
+    {
+      int wid = FRAME_COLUMN_WIDTH (f);
+      FRAME_CONFIG_SCROLL_BAR_COLS (f)
+       = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + wid - 1) / wid;
+    }
+  else
+    {
+      int wid = FRAME_COLUMN_WIDTH (f);
+      FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + wid - 1) / wid;
+    }
+
+  /* Compute the scroll bar height in character lines.  */
+  if (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0)
+    {
+      int height = FRAME_LINE_HEIGHT (f);
+      FRAME_CONFIG_SCROLL_BAR_LINES (f)
+       = (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + height - 1) / height;
+    }
+  else
+    {
+      int height = FRAME_LINE_HEIGHT (f);
+      FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
+    }
+
+  /* Now make the frame display the given font.  */
+  if (FRAME_GTK_WIDGET (f) != NULL)
+    adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
+                      FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
+                      false, Qfont);
+
+  PGTK_TRACE("set new.");
+  return font_object;
+}
+
+int
+x_display_pixel_height (struct pgtk_display_info *dpyinfo)
+{
+  PGTK_TRACE("x_display_pixel_height");
+
+  GdkDisplay *gdpy = dpyinfo->gdpy;
+  GdkScreen *gscr = gdk_display_get_default_screen(gdpy);
+  PGTK_TRACE(" = %d", gdk_screen_get_height(gscr));
+  return gdk_screen_get_height(gscr);
+}
+
+int
+x_display_pixel_width (struct pgtk_display_info *dpyinfo)
+{
+  PGTK_TRACE("x_display_pixel_width");
+
+  GdkDisplay *gdpy = dpyinfo->gdpy;
+  GdkScreen *gscr = gdk_display_get_default_screen(gdpy);
+  PGTK_TRACE(" = %d", gdk_screen_get_width(gscr));
+  return gdk_screen_get_width(gscr);
+}
+
+void
+x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, Lisp_Object 
old_value)
+/* Set frame F's `no-focus-on-map' parameter which, if non-nil, means
+ * that F's window-system window does not want to receive input focus
+ * when it is mapped.  (A frame's window is mapped when the frame is
+ * displayed for the first time and when the frame changes its state
+ * from `iconified' or `invisible' to `visible'.)
+ *
+ * Some window managers may not honor this parameter. */
+{
+  PGTK_TRACE("x_set_no_accept_focus_on_map");
+  /* doesn't work on wayland. */
+
+  if (!EQ (new_value, old_value))
+    {
+      xg_set_no_focus_on_map (f, new_value);
+      FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
+    }
+}
+
+void
+x_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object 
old_value)
+/*  Set frame F's `no-accept-focus' parameter which, if non-nil, hints
+ * that F's window-system window does not want to receive input focus
+ * via mouse clicks or by moving the mouse into it.
+ *
+ * If non-nil, this may have the unwanted side-effect that a user cannot
+ * scroll a non-selected frame with the mouse.
+ *
+ * Some window managers may not honor this parameter. */
+{
+  /* doesn't work on wayland. */
+  PGTK_TRACE("x_set_no_accept_focus");
+
+  xg_set_no_accept_focus (f, new_value);
+  FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
+}
+
+void
+x_set_z_group (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
+/* Set frame F's `z-group' parameter.  If `above', F's window-system
+   window is displayed above all windows that do not have the `above'
+   property set.  If nil, F's window is shown below all windows that
+   have the `above' property set and above all windows that have the
+   `below' property set.  If `below', F's window is displayed below
+   all windows that do.
+
+   Some window managers may not honor this parameter. */
+{
+  /* doesn't work on wayland. */
+  PGTK_TRACE("x_set_z_group");
+
+  if (NILP (new_value))
+    {
+      gtk_window_set_keep_above (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), 
FALSE);
+      gtk_window_set_keep_below (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), 
FALSE);
+      FRAME_Z_GROUP (f) = z_group_none;
+    }
+  else if (EQ (new_value, Qabove))
+    {
+      gtk_window_set_keep_above (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), 
TRUE);
+      gtk_window_set_keep_below (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), 
FALSE);
+      FRAME_Z_GROUP (f) = z_group_above;
+    }
+  else if (EQ (new_value, Qabove_suspended))
+    {
+      gtk_window_set_keep_above (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), 
FALSE);
+      FRAME_Z_GROUP (f) = z_group_above_suspended;
+    }
+  else if (EQ (new_value, Qbelow))
+    {
+      gtk_window_set_keep_above (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), 
FALSE);
+      gtk_window_set_keep_below (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), 
TRUE);
+      FRAME_Z_GROUP (f) = z_group_below;
+    }
+  else
+    error ("Invalid z-group specification");
+}
+
+static void
+pgtk_initialize_display_info (struct pgtk_display_info *dpyinfo)
+/* --------------------------------------------------------------------------
+      Initialize global info and storage for display.
+   -------------------------------------------------------------------------- 
*/
+{
+    dpyinfo->resx = 72.27; /* used 75.0, but this makes pt == pixel, expected 
*/
+    dpyinfo->resy = 72.27;
+    dpyinfo->color_p = 1;
+    dpyinfo->n_planes = 32;
+    dpyinfo->root_window = 42; /* a placeholder.. */
+    dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame = NULL;
+    dpyinfo->n_fonts = 0;
+    dpyinfo->smallest_font_height = 1;
+    dpyinfo->smallest_char_width = 1;
+
+    reset_mouse_highlight (&dpyinfo->mouse_highlight);
+}
+
+/* Set S->gc to a suitable GC for drawing glyph string S in cursor
+   face.  */
+
+static void
+x_set_cursor_gc (struct glyph_string *s)
+{
+  PGTK_TRACE("x_set_cursor_gc.");
+  if (s->font == FRAME_FONT (s->f)
+      && s->face->background == FRAME_BACKGROUND_PIXEL (s->f)
+      && s->face->foreground == FRAME_FOREGROUND_PIXEL (s->f)
+      && !s->cmp)
+    PGTK_TRACE("x_set_cursor_gc: 1."),
+    s->xgcv = FRAME_X_OUTPUT(s->f)->cursor_xgcv;
+  else
+    {
+      /* Cursor on non-default face: must merge.  */
+      XGCValues xgcv;
+
+      PGTK_TRACE("x_set_cursor_gc: 2.");
+      xgcv.background = FRAME_X_OUTPUT(s->f)->cursor_color;
+      xgcv.foreground = s->face->background;
+      PGTK_TRACE("x_set_cursor_gc: 3. %08lx, %08lx.", xgcv.background, 
xgcv.foreground);
+
+      /* If the glyph would be invisible, try a different foreground.  */
+      if (xgcv.foreground == xgcv.background)
+       xgcv.foreground = s->face->foreground;
+      PGTK_TRACE("x_set_cursor_gc: 4. %08lx, %08lx.", xgcv.background, 
xgcv.foreground);
+#if 0
+      if (xgcv.foreground == xgcv.background)
+       xgcv.foreground = FRAME_X_OUTPUT(s->f)->cursor_foreground_pixel;
+#endif
+      if (xgcv.foreground == xgcv.background)
+       xgcv.foreground = s->face->foreground;
+      PGTK_TRACE("x_set_cursor_gc: 5. %08lx, %08lx.", xgcv.background, 
xgcv.foreground);
+
+      /* Make sure the cursor is distinct from text in this face.  */
+      if (xgcv.background == s->face->background
+         && xgcv.foreground == s->face->foreground)
+       {
+         xgcv.background = s->face->foreground;
+         xgcv.foreground = s->face->background;
+       }
+      PGTK_TRACE("x_set_cursor_gc: 6. %08lx, %08lx.", xgcv.background, 
xgcv.foreground);
+
+      IF_DEBUG (x_check_font (s->f, s->font));
+
+      s->xgcv = xgcv;
+    }
+}
+
+
+/* Set up S->gc of glyph string S for drawing text in mouse face.  */
+
+static void
+x_set_mouse_face_gc (struct glyph_string *s)
+{
+  int face_id;
+  struct face *face;
+
+  /* What face has to be used last for the mouse face?  */
+  face_id = MOUSE_HL_INFO (s->f)->mouse_face_face_id;
+  face = FACE_FROM_ID_OR_NULL (s->f, face_id);
+  if (face == NULL)
+    face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+
+  if (s->first_glyph->type == CHAR_GLYPH)
+    face_id = FACE_FOR_CHAR (s->f, face, s->first_glyph->u.ch, -1, Qnil);
+  else
+    face_id = FACE_FOR_CHAR (s->f, face, 0, -1, Qnil);
+  s->face = FACE_FROM_ID (s->f, face_id);
+  prepare_face_for_display (s->f, s->face);
+
+  if (s->font == s->face->font) {
+    s->xgcv.foreground = s->face->foreground;
+    s->xgcv.background = s->face->background;
+  } else
+    {
+      /* Otherwise construct scratch_cursor_gc with values from FACE
+        except for FONT.  */
+      XGCValues xgcv;
+
+      xgcv.background = s->face->background;
+      xgcv.foreground = s->face->foreground;
+
+      s->xgcv = xgcv;
+
+    }
+}
+
+
+/* Set S->gc of glyph string S to a GC suitable for drawing a mode line.
+   Faces to use in the mode line have already been computed when the
+   matrix was built, so there isn't much to do, here.  */
+
+static void
+x_set_mode_line_face_gc (struct glyph_string *s)
+{
+  s->xgcv.foreground = s->face->foreground;
+  s->xgcv.background = s->face->background;
+}
+
+
+/* Set S->gc of glyph string S for drawing that glyph string.  Set
+   S->stippled_p to a non-zero value if the face of S has a stipple
+   pattern.  */
+
+static void
+x_set_glyph_string_gc (struct glyph_string *s)
+{
+  PGTK_TRACE("x_set_glyph_string_gc: s->f:    %08lx, %08lx", 
s->f->background_pixel, s->f->foreground_pixel);
+  PGTK_TRACE("x_set_glyph_string_gc: s->face: %08lx, %08lx", 
s->face->background, s->face->foreground);
+  prepare_face_for_display (s->f, s->face);
+  PGTK_TRACE("x_set_glyph_string_gc: s->face: %08lx, %08lx", 
s->face->background, s->face->foreground);
+
+  if (s->hl == DRAW_NORMAL_TEXT)
+    {
+      s->xgcv.foreground = s->face->foreground;
+      s->xgcv.background = s->face->background;
+      s->stippled_p = s->face->stipple != 0;
+      PGTK_TRACE("x_set_glyph_string_gc: %08lx, %08lx", s->xgcv.background, 
s->xgcv.foreground);
+    }
+  else if (s->hl == DRAW_INVERSE_VIDEO)
+    {
+      x_set_mode_line_face_gc (s);
+      s->stippled_p = s->face->stipple != 0;
+      PGTK_TRACE("x_set_glyph_string_gc: %08lx, %08lx", s->xgcv.background, 
s->xgcv.foreground);
+    }
+  else if (s->hl == DRAW_CURSOR)
+    {
+      x_set_cursor_gc (s);
+      s->stippled_p = false;
+      PGTK_TRACE("x_set_glyph_string_gc: %08lx, %08lx", s->xgcv.background, 
s->xgcv.foreground);
+    }
+  else if (s->hl == DRAW_MOUSE_FACE)
+    {
+      x_set_mouse_face_gc (s);
+      s->stippled_p = s->face->stipple != 0;
+      PGTK_TRACE("x_set_glyph_string_gc: %08lx, %08lx", s->xgcv.background, 
s->xgcv.foreground);
+    }
+  else if (s->hl == DRAW_IMAGE_RAISED
+          || s->hl == DRAW_IMAGE_SUNKEN)
+    {
+      s->xgcv.foreground = s->face->foreground;
+      s->xgcv.background = s->face->background;
+      s->stippled_p = s->face->stipple != 0;
+      PGTK_TRACE("x_set_glyph_string_gc: %08lx, %08lx", s->xgcv.background, 
s->xgcv.foreground);
+    }
+  else
+    emacs_abort ();
+}
+
+
+/* Set clipping for output of glyph string S.  S may be part of a mode
+   line or menu if we don't have X toolkit support.  */
+
+static void
+x_set_glyph_string_clipping (struct glyph_string *s, cairo_t *cr)
+{
+  XRectangle r[2];
+  int n = get_glyph_string_clip_rects (s, r, 2);
+  PGTK_TRACE("x_set_glyph_string_clipping: n=%d.", n);
+
+  if (n > 0) {
+    for (int i = 0; i < n; i++) {
+      PGTK_TRACE("x_set_glyph_string_clipping: r[%d]: %ux%u+%d+%d.",
+                i, r[i].width, r[i].height, r[i].x, r[i].y);
+      cairo_rectangle(cr, r[i].x, r[i].y, r[i].width, r[i].height);
+    }
+    cairo_clip(cr);
+  }
+  PGTK_TRACE("clip result:");
+  cairo_rectangle_list_t *rects = cairo_copy_clip_rectangle_list(cr);
+  for (int i = 0; i < rects->num_rectangles; i++) {
+    PGTK_TRACE(" rect[%d]: %dx%d+%d+%d.",
+              i,
+              (int) rects->rectangles[i].width,
+              (int) rects->rectangles[i].height,
+              (int) rects->rectangles[i].x,
+              (int) rects->rectangles[i].y);
+  }
+  cairo_rectangle_list_destroy(rects);
+}
+
+
+/* Set SRC's clipping for output of glyph string DST.  This is called
+   when we are drawing DST's left_overhang or right_overhang only in
+   the area of SRC.  */
+
+static void
+x_set_glyph_string_clipping_exactly (struct glyph_string *src, struct 
glyph_string *dst, cairo_t *cr)
+{
+  dst->clip[0].x = src->x;
+  dst->clip[0].y = src->y;
+  dst->clip[0].width = src->width;
+  dst->clip[0].height = src->height;
+  dst->num_clips = 1;
+
+  cairo_rectangle(cr, src->x, src->y, src->width, src->height);
+  cairo_clip(cr);
+}
+
+
+/* RIF:
+   Compute left and right overhang of glyph string S.  */
+
+static void
+pgtk_compute_glyph_string_overhangs (struct glyph_string *s)
+{
+  if (s->cmp == NULL
+      && (s->first_glyph->type == CHAR_GLYPH
+         || s->first_glyph->type == COMPOSITE_GLYPH))
+    {
+      struct font_metrics metrics;
+
+      if (s->first_glyph->type == CHAR_GLYPH)
+       {
+         unsigned *code = alloca (sizeof (unsigned) * s->nchars);
+         struct font *font = s->font;
+         int i;
+
+         for (i = 0; i < s->nchars; i++)
+           code[i] = s->char2b[i];
+         font->driver->text_extents (font, code, s->nchars, &metrics);
+       }
+      else
+       {
+         Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+
+         composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
+       }
+      s->right_overhang = (metrics.rbearing > metrics.width
+                          ? metrics.rbearing - metrics.width : 0);
+      s->left_overhang = metrics.lbearing < 0 ? - metrics.lbearing : 0;
+    }
+  else if (s->cmp)
+    {
+      s->right_overhang = s->cmp->rbearing - s->cmp->pixel_width;
+      s->left_overhang = - s->cmp->lbearing;
+    }
+}
+
+
+/* Fill rectangle X, Y, W, H with background color of glyph string S.  */
+
+static void
+x_clear_glyph_string_rect (struct glyph_string *s, int x, int y, int w, int h)
+{
+  pgtk_fill_rectangle(s->f, s->xgcv.background, x, y, w, h);
+}
+
+
+/* Draw the background of glyph_string S.  If S->background_filled_p
+   is non-zero don't draw it.  FORCE_P non-zero means draw the
+   background even if it wouldn't be drawn normally.  This is used
+   when a string preceding S draws into the background of S, or S
+   contains the first component of a composition.  */
+
+static void
+x_draw_glyph_string_background (struct glyph_string *s, bool force_p)
+{
+  PGTK_TRACE("x_draw_glyph_string_background: 0.");
+  /* Nothing to do if background has already been drawn or if it
+     shouldn't be drawn in the first place.  */
+  if (!s->background_filled_p)
+    {
+      PGTK_TRACE("x_draw_glyph_string_background: 1.");
+      int box_line_width = max (s->face->box_line_width, 0);
+
+      PGTK_TRACE("x_draw_glyph_string_background: 2. %d, %d.",
+                  FONT_HEIGHT (s->font), s->height - 2 * box_line_width);
+      PGTK_TRACE("x_draw_glyph_string_background: 2. %d.", 
FONT_TOO_HIGH(s->font));
+      PGTK_TRACE("x_draw_glyph_string_background: 2. %d.", 
s->font_not_found_p);
+      PGTK_TRACE("x_draw_glyph_string_background: 2. %d.", 
s->extends_to_end_of_line_p);
+      PGTK_TRACE("x_draw_glyph_string_background: 2. %d.", force_p);
+#if 0
+      if (s->stippled_p)
+       {
+         /* Fill background with a stipple pattern.  */
+         XSetFillStyle (s->display, s->gc, FillOpaqueStippled);
+         x_fill_rectangle (s->f, s->gc, s->x,
+                         s->y + box_line_width,
+                         s->background_width,
+                         s->height - 2 * box_line_width);
+         XSetFillStyle (s->display, s->gc, FillSolid);
+         s->background_filled_p = true;
+       }
+      else
+#endif
+       if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
+              /* When xdisp.c ignores FONT_HEIGHT, we cannot trust
+                 font dimensions, since the actual glyphs might be
+                 much smaller.  So in that case we always clear the
+                 rectangle with background color.  */
+              || FONT_TOO_HIGH (s->font)
+              || s->font_not_found_p
+              || s->extends_to_end_of_line_p
+              || force_p)
+       {
+         PGTK_TRACE("x_draw_glyph_string_background: 3.");
+         x_clear_glyph_string_rect (s, s->x, s->y + box_line_width,
+                                    s->background_width,
+                                    s->height - 2 * box_line_width);
+         s->background_filled_p = true;
+       }
+    }
+}
+
+
+static void
+pgtk_draw_rectangle (struct frame *f, unsigned long color, int x, int y, int 
width, int height)
+{
+  cairo_t *cr;
+
+  cr = pgtk_begin_cr_clip (f);
+  pgtk_set_cr_source_with_color (f, color);
+  cairo_rectangle (cr, x + 0.5, y + 0.5, width, height);
+  cairo_set_line_width (cr, 1);
+  cairo_stroke (cr);
+  pgtk_end_cr_clip (f);
+}
+
+/* Draw the foreground of glyph string S.  */
+
+static void
+x_draw_glyph_string_foreground (struct glyph_string *s)
+{
+  int i, x;
+
+  /* If first glyph of S has a left box line, start drawing the text
+     of S to the right of that box line.  */
+  if (s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + eabs (s->face->box_line_width);
+  else
+    x = s->x;
+
+  /* Draw characters of S as rectangles if S's font could not be
+     loaded.  */
+  if (s->font_not_found_p)
+    {
+      for (i = 0; i < s->nchars; ++i)
+       {
+         struct glyph *g = s->first_glyph + i;
+         pgtk_draw_rectangle (s->f,
+                                s->face->foreground, x, s->y, g->pixel_width - 
1,
+                                s->height - 1);
+         x += g->pixel_width;
+       }
+    }
+  else
+    {
+      struct font *font = s->font;
+      int boff = font->baseline_offset;
+      int y;
+
+      if (font->vertical_centering)
+       boff = VCENTER_BASELINE_OFFSET (font, s->f) - boff;
+
+      y = s->ybase - boff;
+      if (s->for_overlaps
+         || (s->background_filled_p && s->hl != DRAW_CURSOR))
+       font->driver->draw (s, 0, s->nchars, x, y, false);
+      else
+       font->driver->draw (s, 0, s->nchars, x, y, true);
+      if (s->face->overstrike)
+       font->driver->draw (s, 0, s->nchars, x + 1, y, false);
+    }
+}
+
+/* Draw the foreground of composite glyph string S.  */
+
+static void
+x_draw_composite_glyph_string_foreground (struct glyph_string *s)
+{
+  int i, j, x;
+  struct font *font = s->font;
+
+  /* If first glyph of S has a left box line, start drawing the text
+     of S to the right of that box line.  */
+  if (s->face && s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + eabs (s->face->box_line_width);
+  else
+    x = s->x;
+
+  /* S is a glyph string for a composition.  S->cmp_from is the index
+     of the first character drawn for glyphs of this composition.
+     S->cmp_from == 0 means we are drawing the very first character of
+     this composition.  */
+
+  /* Draw a rectangle for the composition if the font for the very
+     first character of the composition could not be loaded.  */
+  if (s->font_not_found_p)
+    {
+      if (s->cmp_from == 0)
+       pgtk_draw_rectangle (s->f, s->face->foreground, x, s->y,
+                              s->width - 1, s->height - 1);
+    }
+  else if (! s->first_glyph->u.cmp.automatic)
+    {
+      int y = s->ybase;
+
+      for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
+       /* TAB in a composition means display glyphs with padding
+          space on the left or right.  */
+       if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
+         {
+           int xx = x + s->cmp->offsets[j * 2];
+           int yy = y - s->cmp->offsets[j * 2 + 1];
+
+           font->driver->draw (s, j, j + 1, xx, yy, false);
+           if (s->face->overstrike)
+             font->driver->draw (s, j, j + 1, xx + 1, yy, false);
+         }
+    }
+  else
+    {
+      Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+      Lisp_Object glyph;
+      int y = s->ybase;
+      int width = 0;
+
+      for (i = j = s->cmp_from; i < s->cmp_to; i++)
+       {
+         glyph = LGSTRING_GLYPH (gstring, i);
+         if (NILP (LGLYPH_ADJUSTMENT (glyph)))
+           width += LGLYPH_WIDTH (glyph);
+         else
+           {
+             int xoff, yoff, wadjust;
+
+             if (j < i)
+               {
+                 font->driver->draw (s, j, i, x, y, false);
+                 if (s->face->overstrike)
+                   font->driver->draw (s, j, i, x + 1, y, false);
+                 x += width;
+               }
+             xoff = LGLYPH_XOFF (glyph);
+             yoff = LGLYPH_YOFF (glyph);
+             wadjust = LGLYPH_WADJUST (glyph);
+             font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
+             if (s->face->overstrike)
+               font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
+                                   false);
+             x += wadjust;
+             j = i + 1;
+             width = 0;
+           }
+       }
+      if (j < i)
+       {
+         font->driver->draw (s, j, i, x, y, false);
+         if (s->face->overstrike)
+           font->driver->draw (s, j, i, x + 1, y, false);
+       }
+    }
+}
+
+
+/* Draw the foreground of glyph string S for glyphless characters.  */
+
+static void
+x_draw_glyphless_glyph_string_foreground (struct glyph_string *s)
+{
+  struct glyph *glyph = s->first_glyph;
+  unsigned char2b[8];
+  int x, i, j;
+
+  /* If first glyph of S has a left box line, start drawing the text
+     of S to the right of that box line.  */
+  if (s->face && s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + eabs (s->face->box_line_width);
+  else
+    x = s->x;
+
+  s->char2b = char2b;
+
+  for (i = 0; i < s->nchars; i++, glyph++)
+    {
+#ifdef GCC_LINT
+      enum { PACIFY_GCC_BUG_81401 = 1 };
+#else
+      enum { PACIFY_GCC_BUG_81401 = 0 };
+#endif
+      char buf[7 + PACIFY_GCC_BUG_81401];
+      char *str = NULL;
+      int len = glyph->u.glyphless.len;
+
+      if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM)
+       {
+         if (len > 0
+             && CHAR_TABLE_P (Vglyphless_char_display)
+             && (CHAR_TABLE_EXTRA_SLOTS (XCHAR_TABLE (Vglyphless_char_display))
+                 >= 1))
+           {
+             Lisp_Object acronym
+               = (! glyph->u.glyphless.for_no_font
+                  ? CHAR_TABLE_REF (Vglyphless_char_display,
+                                    glyph->u.glyphless.ch)
+                  : XCHAR_TABLE (Vglyphless_char_display)->extras[0]);
+             if (STRINGP (acronym))
+               str = SSDATA (acronym);
+           }
+       }
+      else if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEX_CODE)
+       {
+         unsigned int ch = glyph->u.glyphless.ch;
+         eassume (ch <= MAX_CHAR);
+         sprintf (buf, "%0*X", ch < 0x10000 ? 4 : 6, ch);
+         str = buf;
+       }
+
+      if (str)
+       {
+         int upper_len = (len + 1) / 2;
+
+         /* It is assured that all LEN characters in STR is ASCII.  */
+         for (j = 0; j < len; j++)
+           char2b[j] = s->font->driver->encode_char (s->font, str[j]) & 0xFFFF;
+         s->font->driver->draw (s, 0, upper_len,
+                                x + glyph->slice.glyphless.upper_xoff,
+                                s->ybase + glyph->slice.glyphless.upper_yoff,
+                                false);
+         s->font->driver->draw (s, upper_len, len,
+                                x + glyph->slice.glyphless.lower_xoff,
+                                s->ybase + glyph->slice.glyphless.lower_yoff,
+                                false);
+       }
+      if (glyph->u.glyphless.method != GLYPHLESS_DISPLAY_THIN_SPACE)
+       pgtk_draw_rectangle (s->f, s->face->foreground,
+                              x, s->ybase - glyph->ascent,
+                              glyph->pixel_width - 1,
+                              glyph->ascent + glyph->descent - 1);
+      x += glyph->pixel_width;
+   }
+}
+
+/* Brightness beyond which a color won't have its highlight brightness
+   boosted.
+
+   Nominally, highlight colors for `3d' faces are calculated by
+   brightening an object's color by a constant scale factor, but this
+   doesn't yield good results for dark colors, so for colors who's
+   brightness is less than this value (on a scale of 0-65535) have an
+   use an additional additive factor.
+
+   The value here is set so that the default menu-bar/mode-line color
+   (grey75) will not have its highlights changed at all.  */
+#define HIGHLIGHT_COLOR_DARK_BOOST_LIMIT 48000
+
+
+/* Allocate a color which is lighter or darker than *PIXEL by FACTOR
+   or DELTA.  Try a color with RGB values multiplied by FACTOR first.
+   If this produces the same color as PIXEL, try a color where all RGB
+   values have DELTA added.  Return the allocated color in *PIXEL.
+   DISPLAY is the X display, CMAP is the colormap to operate on.
+   Value is non-zero if successful.  */
+
+static bool
+x_alloc_lighter_color (struct frame *f, unsigned long *pixel, double factor, 
int delta)
+{
+  Emacs_Color color, new;
+  long bright;
+  bool success_p;
+
+  /* Get RGB color values.  */
+  color.pixel = *pixel;
+  pgtk_query_color (f, &color);
+
+  /* Change RGB values by specified FACTOR.  Avoid overflow!  */
+  eassert (factor >= 0);
+  new.red = min (0xffff, factor * color.red);
+  new.green = min (0xffff, factor * color.green);
+  new.blue = min (0xffff, factor * color.blue);
+
+  /* Calculate brightness of COLOR.  */
+  bright = (2 * color.red + 3 * color.green + color.blue) / 6;
+
+  /* We only boost colors that are darker than
+     HIGHLIGHT_COLOR_DARK_BOOST_LIMIT.  */
+  if (bright < HIGHLIGHT_COLOR_DARK_BOOST_LIMIT)
+    /* Make an additive adjustment to NEW, because it's dark enough so
+       that scaling by FACTOR alone isn't enough.  */
+    {
+      /* How far below the limit this color is (0 - 1, 1 being darker).  */
+      double dimness = 1 - (double)bright / HIGHLIGHT_COLOR_DARK_BOOST_LIMIT;
+      /* The additive adjustment.  */
+      int min_delta = delta * dimness * factor / 2;
+
+      if (factor < 1)
+       {
+         new.red =   max (0, new.red -   min_delta);
+         new.green = max (0, new.green - min_delta);
+         new.blue =  max (0, new.blue -  min_delta);
+       }
+      else
+       {
+         new.red =   min (0xffff, min_delta + new.red);
+         new.green = min (0xffff, min_delta + new.green);
+         new.blue =  min (0xffff, min_delta + new.blue);
+       }
+    }
+
+  /* Try to allocate the color.  */
+  new.pixel = new.red >> 8 << 16 | new.green >> 8 << 8 | new.blue >> 8;
+  success_p = true;
+  if (success_p)
+    {
+      if (new.pixel == *pixel)
+       {
+         /* If we end up with the same color as before, try adding
+            delta to the RGB values.  */
+         new.red = min (0xffff, delta + color.red);
+         new.green = min (0xffff, delta + color.green);
+         new.blue = min (0xffff, delta + color.blue);
+         new.pixel = new.red >> 8 << 16 | new.green >> 8 << 8 | new.blue >> 8;
+         success_p = true;
+       }
+      else
+       success_p = true;
+      *pixel = new.pixel;
+    }
+
+  return success_p;
+}
+
+static void
+x_fill_trapezoid_for_relief (struct frame *f, unsigned long color, int x, int 
y,
+                            int width, int height, int top_p)
+{
+  cairo_t *cr;
+
+  cr = pgtk_begin_cr_clip (f);
+  pgtk_set_cr_source_with_color (f, color);
+  cairo_move_to (cr, top_p ? x : x + height, y);
+  cairo_line_to (cr, x, y + height);
+  cairo_line_to (cr, top_p ? x + width - height : x + width, y + height);
+  cairo_line_to (cr, x + width, y);
+  cairo_fill (cr);
+  pgtk_end_cr_clip (f);
+}
+
+enum corners
+  {
+    CORNER_BOTTOM_RIGHT,       /* 0 -> pi/2 */
+    CORNER_BOTTOM_LEFT,                /* pi/2 -> pi */
+    CORNER_TOP_LEFT,           /* pi -> 3pi/2 */
+    CORNER_TOP_RIGHT,          /* 3pi/2 -> 2pi */
+    CORNER_LAST
+  };
+
+static void
+x_erase_corners_for_relief (struct frame *f, unsigned long color, int x, int y,
+                           int width, int height,
+                           double radius, double margin, int corners)
+{
+  cairo_t *cr;
+  int i;
+
+  cr = pgtk_begin_cr_clip (f);
+  pgtk_set_cr_source_with_color (f, color);
+  for (i = 0; i < CORNER_LAST; i++)
+    if (corners & (1 << i))
+      {
+       double xm, ym, xc, yc;
+
+       if (i == CORNER_TOP_LEFT || i == CORNER_BOTTOM_LEFT)
+         xm = x - margin, xc = xm + radius;
+       else
+         xm = x + width + margin, xc = xm - radius;
+       if (i == CORNER_TOP_LEFT || i == CORNER_TOP_RIGHT)
+         ym = y - margin, yc = ym + radius;
+       else
+         ym = y + height + margin, yc = ym - radius;
+
+       cairo_move_to (cr, xm, ym);
+       cairo_arc (cr, xc, yc, radius, i * M_PI_2, (i + 1) * M_PI_2);
+      }
+  cairo_clip (cr);
+  cairo_rectangle (cr, x, y, width, height);
+  cairo_fill (cr);
+  pgtk_end_cr_clip (f);
+}
+
+/* Set up the foreground color for drawing relief lines of glyph
+   string S.  RELIEF is a pointer to a struct relief containing the GC
+   with which lines will be drawn.  Use a color that is FACTOR or
+   DELTA lighter or darker than the relief's background which is found
+   in S->f->output_data.pgtk->relief_background.  If such a color cannot
+   be allocated, use DEFAULT_PIXEL, instead.  */
+
+static void
+x_setup_relief_color (struct frame *f, struct relief *relief, double factor,
+                     int delta, unsigned long default_pixel)
+{
+  XGCValues xgcv;
+  struct pgtk_output *di = FRAME_X_OUTPUT(f);
+  unsigned long pixel;
+  unsigned long background = di->relief_background;
+
+  /* Allocate new color.  */
+  xgcv.foreground = default_pixel;
+  pixel = background;
+  if (x_alloc_lighter_color (f, &pixel, factor, delta))
+    xgcv.foreground = relief->pixel = pixel;
+
+  relief->xgcv = xgcv;
+}
+
+/* Set up colors for the relief lines around glyph string S.  */
+
+static void
+x_setup_relief_colors (struct glyph_string *s)
+{
+  struct pgtk_output *di = FRAME_X_OUTPUT(s->f);
+  unsigned long color;
+
+  if (s->face->use_box_color_for_shadows_p)
+    color = s->face->box_color;
+  else if (s->first_glyph->type == IMAGE_GLYPH
+          && s->img->pixmap
+          && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
+    color = IMAGE_BACKGROUND (s->img, s->f, 0);
+  else
+    {
+      /* Get the background color of the face.  */
+      color = s->xgcv.background;
+    }
+
+  if (TRUE)
+    {
+      di->relief_background = color;
+      x_setup_relief_color (s->f, &di->white_relief, 1.2, 0x8000,
+                           WHITE_PIX_DEFAULT (s->f));
+      x_setup_relief_color (s->f, &di->black_relief, 0.6, 0x4000,
+                           BLACK_PIX_DEFAULT (s->f));
+    }
+}
+
+
+static void
+x_set_clip_rectangles (struct frame *f, cairo_t *cr, XRectangle *rectangles, 
int n)
+{
+  if (n > 0) {
+    for (int i = 0; i < n; i++) {
+      cairo_rectangle(cr,
+                     rectangles[i].x,
+                     rectangles[i].y,
+                     rectangles[i].width,
+                     rectangles[i].height);
+    }
+    cairo_clip(cr);
+  }
+}
+
+/* Draw a relief on frame F inside the rectangle given by LEFT_X,
+   TOP_Y, RIGHT_X, and BOTTOM_Y.  WIDTH is the thickness of the relief
+   to draw, it must be >= 0.  RAISED_P means draw a raised
+   relief.  LEFT_P means draw a relief on the left side of
+   the rectangle.  RIGHT_P means draw a relief on the right
+   side of the rectangle.  CLIP_RECT is the clipping rectangle to use
+   when drawing.  */
+
+static void
+x_draw_relief_rect (struct frame *f,
+                   int left_x, int top_y, int right_x, int bottom_y,
+                   int width, bool raised_p, bool top_p, bool bot_p,
+                   bool left_p, bool right_p,
+                   XRectangle *clip_rect)
+{
+  unsigned long top_left_color, bottom_right_color;
+  int corners = 0;
+
+  cairo_t *cr = pgtk_begin_cr_clip(f);
+
+  if (raised_p)
+    {
+      top_left_color = FRAME_X_OUTPUT(f)->white_relief.xgcv.foreground;
+      bottom_right_color = FRAME_X_OUTPUT(f)->black_relief.xgcv.foreground;
+    }
+  else
+    {
+      top_left_color = FRAME_X_OUTPUT(f)->black_relief.xgcv.foreground;
+      bottom_right_color = FRAME_X_OUTPUT(f)->white_relief.xgcv.foreground;
+    }
+
+  x_set_clip_rectangles (f, cr, clip_rect, 1);
+
+  if (left_p)
+    {
+      pgtk_fill_rectangle (f, top_left_color, left_x, top_y,
+                            width, bottom_y + 1 - top_y);
+      if (top_p)
+       corners |= 1 << CORNER_TOP_LEFT;
+      if (bot_p)
+       corners |= 1 << CORNER_BOTTOM_LEFT;
+    }
+  if (right_p)
+    {
+      pgtk_fill_rectangle (f, bottom_right_color, right_x + 1 - width, top_y,
+                            width, bottom_y + 1 - top_y);
+      if (top_p)
+       corners |= 1 << CORNER_TOP_RIGHT;
+      if (bot_p)
+       corners |= 1 << CORNER_BOTTOM_RIGHT;
+    }
+  if (top_p)
+    {
+      if (!right_p)
+       pgtk_fill_rectangle (f, top_left_color, left_x, top_y,
+                              right_x + 1 - left_x, width);
+      else
+       x_fill_trapezoid_for_relief (f, top_left_color, left_x, top_y,
+                                    right_x + 1 - left_x, width, 1);
+    }
+  if (bot_p)
+    {
+      if (!left_p)
+       pgtk_fill_rectangle (f, bottom_right_color, left_x, bottom_y + 1 - 
width,
+                              right_x + 1 - left_x, width);
+      else
+       x_fill_trapezoid_for_relief (f, bottom_right_color,
+                                    left_x, bottom_y + 1 - width,
+                                    right_x + 1 - left_x, width, 0);
+    }
+  if (left_p && width != 1)
+    pgtk_fill_rectangle (f, bottom_right_color, left_x, top_y,
+                          1, bottom_y + 1 - top_y);
+  if (top_p && width != 1)
+    pgtk_fill_rectangle (f, bottom_right_color, left_x, top_y,
+                          right_x + 1 - left_x, 1);
+  if (corners)
+    {
+      x_erase_corners_for_relief (f, FRAME_BACKGROUND_PIXEL (f), left_x, top_y,
+                                 right_x - left_x + 1, bottom_y - top_y + 1,
+                                 6, 1, corners);
+    }
+
+  pgtk_end_cr_clip(f);
+}
+
+/* Draw a box on frame F inside the rectangle given by LEFT_X, TOP_Y,
+   RIGHT_X, and BOTTOM_Y.  WIDTH is the thickness of the lines to
+   draw, it must be >= 0.  LEFT_P means draw a line on the
+   left side of the rectangle.  RIGHT_P means draw a line
+   on the right side of the rectangle.  CLIP_RECT is the clipping
+   rectangle to use when drawing.  */
+
+static void
+x_draw_box_rect (struct glyph_string *s,
+                int left_x, int top_y, int right_x, int bottom_y, int width,
+                bool left_p, bool right_p, XRectangle *clip_rect)
+{
+  unsigned long foreground_backup;
+
+  cairo_t *cr = pgtk_begin_cr_clip(s->f);
+
+  foreground_backup = s->xgcv.foreground;
+  s->xgcv.foreground = s->face->box_color;
+
+  x_set_clip_rectangles (s->f, cr, clip_rect, 1);
+
+  /* Top.  */
+  pgtk_fill_rectangle (s->f, s->xgcv.foreground,
+                        left_x, top_y, right_x - left_x + 1, width);
+
+  /* Left.  */
+  if (left_p)
+    pgtk_fill_rectangle (s->f, s->xgcv.foreground,
+                          left_x, top_y, width, bottom_y - top_y + 1);
+
+  /* Bottom.  */
+  pgtk_fill_rectangle (s->f, s->xgcv.foreground,
+                        left_x, bottom_y - width + 1, right_x - left_x + 1, 
width);
+
+  /* Right.  */
+  if (right_p)
+    pgtk_fill_rectangle (s->f, s->xgcv.foreground,
+                          right_x - width + 1, top_y, width, bottom_y - top_y 
+ 1);
+
+  s->xgcv.foreground = foreground_backup;
+
+  pgtk_end_cr_clip(s->f);
+}
+
+
+/* Draw a box around glyph string S.  */
+
+static void
+x_draw_glyph_string_box (struct glyph_string *s)
+{
+  int width, left_x, right_x, top_y, bottom_y, last_x;
+  bool raised_p, left_p, right_p;
+  struct glyph *last_glyph;
+  XRectangle clip_rect;
+
+  last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
+           ? WINDOW_RIGHT_EDGE_X (s->w)
+           : window_box_right (s->w, s->area));
+
+  /* The glyph that may have a right box line.  */
+  last_glyph = (s->cmp || s->img
+               ? s->first_glyph
+               : s->first_glyph + s->nchars - 1);
+
+  width = eabs (s->face->box_line_width);
+  raised_p = s->face->box == FACE_RAISED_BOX;
+  left_x = s->x;
+  right_x = (s->row->full_width_p && s->extends_to_end_of_line_p
+            ? last_x - 1
+            : min (last_x, s->x + s->background_width) - 1);
+  top_y = s->y;
+  bottom_y = top_y + s->height - 1;
+
+  left_p = (s->first_glyph->left_box_line_p
+           || (s->hl == DRAW_MOUSE_FACE
+               && (s->prev == NULL
+                   || s->prev->hl != s->hl)));
+  right_p = (last_glyph->right_box_line_p
+            || (s->hl == DRAW_MOUSE_FACE
+                && (s->next == NULL
+                    || s->next->hl != s->hl)));
+
+  get_glyph_string_clip_rect (s, &clip_rect);
+
+  if (s->face->box == FACE_SIMPLE_BOX)
+    x_draw_box_rect (s, left_x, top_y, right_x, bottom_y, width,
+                    left_p, right_p, &clip_rect);
+  else
+    {
+      x_setup_relief_colors (s);
+      x_draw_relief_rect (s->f, left_x, top_y, right_x, bottom_y,
+                         width, raised_p, true, true, left_p, right_p,
+                         &clip_rect);
+    }
+}
+
+static void
+x_get_scale_factor(int *scale_x, int *scale_y)
+{
+  *scale_x = *scale_y = 1;
+}
+
+static void
+x_draw_horizontal_wave (struct frame *f, unsigned long color, int x, int y,
+                       int width, int height, int wave_length)
+{
+  cairo_t *cr;
+  double dx = wave_length, dy = height - 1;
+  int xoffset, n;
+
+  cr = pgtk_begin_cr_clip (f);
+  pgtk_set_cr_source_with_color (f, color);
+  cairo_rectangle (cr, x, y, width, height);
+  cairo_clip (cr);
+
+  if (x >= 0)
+    {
+      xoffset = x % (wave_length * 2);
+      if (xoffset == 0)
+       xoffset = wave_length * 2;
+    }
+  else
+    xoffset = x % (wave_length * 2) + wave_length * 2;
+  n = (width + xoffset) / wave_length + 1;
+  if (xoffset > wave_length)
+    {
+      xoffset -= wave_length;
+      --n;
+      y += height - 1;
+      dy = -dy;
+    }
+
+  cairo_move_to (cr, x - xoffset + 0.5, y + 0.5);
+  while (--n >= 0)
+    {
+      cairo_rel_line_to (cr, dx, dy);
+      dy = -dy;
+    }
+  cairo_set_line_width (cr, 1);
+  cairo_stroke (cr);
+  pgtk_end_cr_clip (f);
+}
+
+/*
+   Draw a wavy line under S. The wave fills wave_height pixels from y0.
+
+                    x0         wave_length = 2
+                                 --
+                y0   *   *   *   *   *
+                     |* * * * * * * * *
+    wave_height = 3  | *   *   *   *
+
+*/
+static void
+x_draw_underwave (struct glyph_string *s, unsigned long color)
+{
+  /* Adjust for scale/HiDPI.  */
+  int scale_x, scale_y;
+
+  x_get_scale_factor (&scale_x, &scale_y);
+
+  int wave_height = 3 * scale_y, wave_length = 2 * scale_x;
+
+  x_draw_horizontal_wave (s->f, color, s->x, s->ybase - wave_height + 3,
+                         s->width, wave_height, wave_length);
+}
+
+/* Draw a relief around the image glyph string S.  */
+
+static void
+x_draw_image_relief (struct glyph_string *s)
+{
+  int x1, y1, thick;
+  bool raised_p, top_p, bot_p, left_p, right_p;
+  int extra_x, extra_y;
+  XRectangle r;
+  int x = s->x;
+  int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+
+  /* If first glyph of S has a left box line, start drawing it to the
+     right of that line.  */
+  if (s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p
+      && s->slice.x == 0)
+    x += eabs (s->face->box_line_width);
+
+  /* If there is a margin around the image, adjust x- and y-position
+     by that margin.  */
+  if (s->slice.x == 0)
+    x += s->img->hmargin;
+  if (s->slice.y == 0)
+    y += s->img->vmargin;
+
+  if (s->hl == DRAW_IMAGE_SUNKEN
+      || s->hl == DRAW_IMAGE_RAISED)
+    {
+      thick = tool_bar_button_relief >= 0 ? tool_bar_button_relief : 
DEFAULT_TOOL_BAR_BUTTON_RELIEF;
+      raised_p = s->hl == DRAW_IMAGE_RAISED;
+    }
+  else
+    {
+      thick = eabs (s->img->relief);
+      raised_p = s->img->relief > 0;
+    }
+
+  x1 = x + s->slice.width - 1;
+  y1 = y + s->slice.height - 1;
+
+  extra_x = extra_y = 0;
+  if (s->face->id == TOOL_BAR_FACE_ID)
+    {
+      if (CONSP (Vtool_bar_button_margin)
+         && INTEGERP (XCAR (Vtool_bar_button_margin))
+         && INTEGERP (XCDR (Vtool_bar_button_margin)))
+       {
+         extra_x = XFIXNUM (XCAR (Vtool_bar_button_margin));
+         extra_y = XFIXNUM (XCDR (Vtool_bar_button_margin));
+       }
+      else if (INTEGERP (Vtool_bar_button_margin))
+       extra_x = extra_y = XFIXNUM (Vtool_bar_button_margin);
+    }
+
+  top_p = bot_p = left_p = right_p = false;
+
+  if (s->slice.x == 0)
+    x -= thick + extra_x, left_p = true;
+  if (s->slice.y == 0)
+    y -= thick + extra_y, top_p = true;
+  if (s->slice.x + s->slice.width == s->img->width)
+    x1 += thick + extra_x, right_p = true;
+  if (s->slice.y + s->slice.height == s->img->height)
+    y1 += thick + extra_y, bot_p = true;
+
+  x_setup_relief_colors (s);
+  get_glyph_string_clip_rect (s, &r);
+  x_draw_relief_rect (s->f, x, y, x1, y1, thick, raised_p,
+                     top_p, bot_p, left_p, right_p, &r);
+}
+
+/* Draw part of the background of glyph string S.  X, Y, W, and H
+   give the rectangle to draw.  */
+
+static void
+x_draw_glyph_string_bg_rect (struct glyph_string *s, int x, int y, int w, int 
h)
+{
+#if 0
+  if (s->stippled_p)
+    {
+      /* Fill background with a stipple pattern.  */
+      XSetFillStyle (s->display, s->gc, FillOpaqueStippled);
+      x_fill_rectangle (s->f, s->gc, x, y, w, h);
+      XSetFillStyle (s->display, s->gc, FillSolid);
+    }
+  else
+#endif
+    x_clear_glyph_string_rect (s, x, y, w, h);
+}
+
+/* Draw foreground of image glyph string S.  */
+
+static void
+x_draw_image_foreground (struct glyph_string *s)
+{
+  int x = s->x;
+  int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+
+  /* If first glyph of S has a left box line, start drawing it to the
+     right of that line.  */
+  if (s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p
+      && s->slice.x == 0)
+    x += eabs (s->face->box_line_width);
+
+  /* If there is a margin around the image, adjust x- and y-position
+     by that margin.  */
+  if (s->slice.x == 0)
+    x += s->img->hmargin;
+  if (s->slice.y == 0)
+    y += s->img->vmargin;
+
+  /* Draw a rectangle if image could not be loaded.  */
+  pgtk_draw_rectangle (s->f, s->xgcv.foreground, x, y,
+                      s->slice.width - 1, s->slice.height - 1);
+}
+
+/* Draw image glyph string S.
+
+            s->y
+   s->x      +-------------------------
+            |   s->face->box
+            |
+            |     +-------------------------
+            |     |  s->img->margin
+            |     |
+            |     |       +-------------------
+            |     |       |  the image
+
+ */
+
+static void
+x_draw_image_glyph_string (struct glyph_string *s)
+{
+  int box_line_hwidth = eabs (s->face->box_line_width);
+  int box_line_vwidth = max (s->face->box_line_width, 0);
+  int height;
+  cairo_surface_t *surface = NULL;
+
+  height = s->height;
+  if (s->slice.y == 0)
+    height -= box_line_vwidth;
+  if (s->slice.y + s->slice.height >= s->img->height)
+    height -= box_line_vwidth;
+
+  /* Fill background with face under the image.  Do it only if row is
+     taller than image or if image has a clip mask to reduce
+     flickering.  */
+  s->stippled_p = s->face->stipple != 0;
+  if (height > s->slice.height
+      || s->img->hmargin
+      || s->img->vmargin
+      || s->img->mask
+      || s->img->pixmap == 0
+      || s->stippled_p
+      || s->width != s->background_width)
+    {
+      if (s->img->mask)
+       {
+         /* Create a pixmap as large as the glyph string.  Fill it
+            with the background color.  Copy the image to it, using
+            its mask.  Copy the temporary pixmap to the display.  */
+
+         /* Create a pixmap as large as the glyph string.  */
+         surface = cairo_surface_create_similar(FRAME_CR_SURFACE(s->f), 
CAIRO_CONTENT_COLOR_ALPHA,
+                                                s->background_width,
+                                                s->height);
+
+         /* Don't clip in the following because we're working on the
+            pixmap.  */
+         // XSetClipMask (s->display, s->gc, None);
+
+         /* Fill the pixmap with the background color/stipple.  */
+#if 0
+         if (s->stippled_p)
+           {
+             /* Fill background with a stipple pattern.  */
+             XSetFillStyle (s->display, s->gc, FillOpaqueStippled);
+             XSetTSOrigin (s->display, s->gc, - s->x, - s->y);
+             XFillRectangle (s->display, pixmap, s->gc,
+                             0, 0, s->background_width, s->height);
+             XSetFillStyle (s->display, s->gc, FillSolid);
+             XSetTSOrigin (s->display, s->gc, 0, 0);
+           }
+         else
+#endif
+           {
+             cairo_t *cr = cairo_create(surface);
+             int red = (s->xgcv.background >> 16) & 0xff;
+             int green = (s->xgcv.background >> 8) & 0xff;
+             int blue = (s->xgcv.background >> 0) & 0xff;
+             cairo_set_source_rgb (cr, red / 255.0, green / 255.0, blue / 
255.0);
+             cairo_rectangle(cr, 0, 0, s->background_width, s->height);
+             cairo_fill(cr);
+             cairo_destroy(cr);
+           }
+       }
+      else
+       {
+         int x = s->x;
+         int y = s->y;
+         int width = s->background_width;
+
+         if (s->first_glyph->left_box_line_p
+             && s->slice.x == 0)
+           {
+             x += box_line_hwidth;
+             width -= box_line_hwidth;
+           }
+
+         if (s->slice.y == 0)
+           y += box_line_vwidth;
+
+         x_draw_glyph_string_bg_rect (s, x, y, width, height);
+       }
+
+      s->background_filled_p = true;
+    }
+
+  /* Draw the foreground.  */
+  if (s->img->cr_data)
+    {
+      cairo_t *cr = pgtk_begin_cr_clip (s->f);
+
+      int x = s->x + s->img->hmargin;
+      int y = s->y + s->img->vmargin;
+      int width = s->background_width;
+
+      cairo_translate (cr, x - s->slice.x, y - s->slice.y);
+      cairo_set_source (cr, s->img->cr_data);
+      cairo_rectangle (cr, 0, 0, width, height);
+      cairo_fill (cr);
+      pgtk_end_cr_clip (s->f);
+    }
+  else
+    if (surface != NULL)
+    {
+      cairo_t *cr = pgtk_begin_cr_clip(s->f);
+
+      x_draw_image_foreground_1 (s, surface);
+      x_set_glyph_string_clipping (s, cr);
+
+      cairo_set_source_surface(cr, surface, 0, 0);
+      cairo_rectangle(cr, s->x, s->y, s->background_width, s->height);
+      pgtk_end_cr_clip(s->f);
+    }
+  else
+    x_draw_image_foreground (s);
+
+  /* If we must draw a relief around the image, do it.  */
+  if (s->img->relief
+      || s->hl == DRAW_IMAGE_RAISED
+      || s->hl == DRAW_IMAGE_SUNKEN)
+    x_draw_image_relief (s);
+
+  if (surface != NULL)
+    cairo_surface_destroy(surface);
+}
+
+/* Draw stretch glyph string S.  */
+
+static void
+x_draw_stretch_glyph_string (struct glyph_string *s)
+{
+  eassert (s->first_glyph->type == STRETCH_GLYPH);
+
+  if (s->hl == DRAW_CURSOR
+      && !x_stretch_cursor_p)
+    {
+      /* If `x-stretch-cursor' is nil, don't draw a block cursor as
+        wide as the stretch glyph.  */
+      int width, background_width = s->background_width;
+      int x = s->x;
+
+      if (!s->row->reversed_p)
+       {
+         int left_x = window_box_left_offset (s->w, TEXT_AREA);
+
+         if (x < left_x)
+           {
+             background_width -= left_x - x;
+             x = left_x;
+           }
+       }
+      else
+       {
+         /* In R2L rows, draw the cursor on the right edge of the
+            stretch glyph.  */
+         int right_x = window_box_right (s->w, TEXT_AREA);
+
+         if (x + background_width > right_x)
+           background_width -= x - right_x;
+         x += background_width;
+       }
+      width = min (FRAME_COLUMN_WIDTH (s->f), background_width);
+      if (s->row->reversed_p)
+       x -= width;
+
+      /* Draw cursor.  */
+      x_draw_glyph_string_bg_rect (s, x, s->y, width, s->height);
+
+      /* Clear rest using the GC of the original non-cursor face.  */
+      if (width < background_width)
+       {
+         int y = s->y;
+         int w = background_width - width, h = s->height;
+         XRectangle r;
+         unsigned long color;
+
+         if (!s->row->reversed_p)
+           x += width;
+         else
+           x = s->x;
+         if (s->row->mouse_face_p
+             && cursor_in_mouse_face_p (s->w))
+           {
+             x_set_mouse_face_gc (s);
+             color = s->xgcv.foreground;
+           }
+         else
+           color = s->face->foreground;
+
+         cairo_t *cr = pgtk_begin_cr_clip(s->f);
+
+         get_glyph_string_clip_rect (s, &r);
+         x_set_clip_rectangles (s->f, cr, &r, 1);
+
+#if 0
+         if (s->face->stipple)
+           {
+             /* Fill background with a stipple pattern.  */
+             XSetFillStyle (s->display, gc, FillOpaqueStippled);
+             x_fill_rectangle (s->f, gc, x, y, w, h);
+             XSetFillStyle (s->display, gc, FillSolid);
+           }
+         else
+#endif
+           {
+             pgtk_fill_rectangle(s->f, color, x, y, w, h);
+           }
+
+         pgtk_end_cr_clip(s->f);
+       }
+    }
+  else if (!s->background_filled_p)
+    {
+      int background_width = s->background_width;
+      int x = s->x, left_x = window_box_left_offset (s->w, TEXT_AREA);
+
+      /* Don't draw into left margin, fringe or scrollbar area
+         except for header line and mode line.  */
+      if (x < left_x && !s->row->mode_line_p)
+       {
+         background_width -= left_x - x;
+         x = left_x;
+       }
+      if (background_width > 0)
+       x_draw_glyph_string_bg_rect (s, x, s->y, background_width, s->height);
+    }
+
+  s->background_filled_p = true;
+}
+
+static void pgtk_draw_glyph_string(struct glyph_string *s)
+{
+  PGTK_TRACE("draw_glyph_string.");
+  PGTK_TRACE("draw_glyph_string: x=%d, y=%d, width=%d, height=%d.",
+              s->x, s->y, s->width, s->height);
+
+  bool relief_drawn_p = false;
+
+  /* If S draws into the background of its successors, draw the
+     background of the successors first so that S can draw into it.
+     This makes S->next use XDrawString instead of XDrawImageString.  */
+  if (s->next && s->right_overhang && !s->for_overlaps)
+    {
+      int width;
+      struct glyph_string *next;
+
+      for (width = 0, next = s->next;
+          next && width < s->right_overhang;
+          width += next->width, next = next->next)
+       if (next->first_glyph->type != IMAGE_GLYPH)
+         {
+           cairo_t *cr = pgtk_begin_cr_clip(next->f);
+           PGTK_TRACE("pgtk_draw_glyph_string: 1.");
+           x_set_glyph_string_gc (next);
+           x_set_glyph_string_clipping (next, cr);
+           if (next->first_glyph->type == STRETCH_GLYPH)
+             x_draw_stretch_glyph_string (next);
+           else
+             x_draw_glyph_string_background (next, true);
+           next->num_clips = 0;
+           pgtk_end_cr_clip(next->f);
+         }
+    }
+
+  /* Set up S->gc, set clipping and draw S.  */
+  PGTK_TRACE("pgtk_draw_glyph_string: 2.");
+  x_set_glyph_string_gc (s);
+
+  cairo_t *cr = pgtk_begin_cr_clip(s->f);
+
+  /* Draw relief (if any) in advance for char/composition so that the
+     glyph string can be drawn over it.  */
+  if (!s->for_overlaps
+      && s->face->box != FACE_NO_BOX
+      && (s->first_glyph->type == CHAR_GLYPH
+         || s->first_glyph->type == COMPOSITE_GLYPH))
+
+    {
+      PGTK_TRACE("pgtk_draw_glyph_string: 2.1.");
+      x_set_glyph_string_clipping (s, cr);
+      x_draw_glyph_string_background (s, true);
+      x_draw_glyph_string_box (s);
+      x_set_glyph_string_clipping (s, cr);
+      relief_drawn_p = true;
+    }
+  else if (!s->clip_head /* draw_glyphs didn't specify a clip mask. */
+          && !s->clip_tail
+          && ((s->prev && s->prev->hl != s->hl && s->left_overhang)
+              || (s->next && s->next->hl != s->hl && s->right_overhang)))
+    /* We must clip just this glyph.  left_overhang part has already
+       drawn when s->prev was drawn, and right_overhang part will be
+       drawn later when s->next is drawn. */
+    PGTK_TRACE("pgtk_draw_glyph_string: 2.2."),
+    x_set_glyph_string_clipping_exactly (s, s, cr);
+  else
+    PGTK_TRACE("pgtk_draw_glyph_string: 2.3."),
+    x_set_glyph_string_clipping (s, cr);
+
+  switch (s->first_glyph->type)
+    {
+    case IMAGE_GLYPH:
+      PGTK_TRACE("pgtk_draw_glyph_string: 2.4.");
+      x_draw_image_glyph_string (s);
+      break;
+
+    case XWIDGET_GLYPH:
+      PGTK_TRACE("pgtk_draw_glyph_string: 2.5.");
+      x_draw_xwidget_glyph_string (s);
+      break;
+
+    case STRETCH_GLYPH:
+      PGTK_TRACE("pgtk_draw_glyph_string: 2.6.");
+      x_draw_stretch_glyph_string (s);
+      break;
+
+    case CHAR_GLYPH:
+      PGTK_TRACE("pgtk_draw_glyph_string: 2.7.");
+      if (s->for_overlaps)
+       s->background_filled_p = true;
+      else
+       x_draw_glyph_string_background (s, false);
+      x_draw_glyph_string_foreground (s);
+      break;
+
+    case COMPOSITE_GLYPH:
+      PGTK_TRACE("pgtk_draw_glyph_string: 2.8.");
+      if (s->for_overlaps || (s->cmp_from > 0
+                             && ! s->first_glyph->u.cmp.automatic))
+       s->background_filled_p = true;
+      else
+       x_draw_glyph_string_background (s, true);
+      x_draw_composite_glyph_string_foreground (s);
+      break;
+
+    case GLYPHLESS_GLYPH:
+      PGTK_TRACE("pgtk_draw_glyph_string: 2.9.");
+      if (s->for_overlaps)
+       s->background_filled_p = true;
+      else
+       x_draw_glyph_string_background (s, true);
+      x_draw_glyphless_glyph_string_foreground (s);
+      break;
+
+    default:
+      emacs_abort ();
+    }
+
+  if (!s->for_overlaps)
+    {
+      /* Draw underline.  */
+      if (s->face->underline)
+       {
+         if (s->face->underline == FACE_UNDER_WAVE)
+           {
+             if (s->face->underline_defaulted_p)
+               x_draw_underwave (s, s->xgcv.foreground);
+             else
+               {
+                 x_draw_underwave (s, s->face->underline_color);
+               }
+           }
+         else if (s->face->underline == FACE_UNDER_LINE)
+           {
+             unsigned long thickness, position;
+             int y;
+
+             if (s->prev && s->prev->face->underline
+                 && s->prev->face->underline == FACE_UNDER_LINE)
+               {
+                 /* We use the same underline style as the previous one.  */
+                 thickness = s->prev->underline_thickness;
+                 position = s->prev->underline_position;
+               }
+             else
+               {
+                 struct font *font = font_for_underline_metrics (s);
+
+                  /* Get the underline thickness.  Default is 1 pixel.  */
+                  if (font && font->underline_thickness > 0)
+                    thickness = font->underline_thickness;
+                  else
+                    thickness = 1;
+                  if (x_underline_at_descent_line)
+                    position = (s->height - thickness) - (s->ybase - s->y);
+                  else
+                    {
+                      /* Get the underline position.  This is the recommended
+                         vertical offset in pixels from the baseline to the 
top of
+                         the underline.  This is a signed value according to 
the
+                         specs, and its default is
+
+                         ROUND ((maximum descent) / 2), with
+                         ROUND(x) = floor (x + 0.5)  */
+
+                      if (x_use_underline_position_properties
+                          && font && font->underline_position >= 0)
+                        position = font->underline_position;
+                      else if (font)
+                        position = (font->descent + 1) / 2;
+                      else
+                        position = underline_minimum_offset;
+                    }
+                  position = max (position, underline_minimum_offset);
+                }
+              /* Check the sanity of thickness and position.  We should
+                 avoid drawing underline out of the current line area.  */
+              if (s->y + s->height <= s->ybase + position)
+                position = (s->height - 1) - (s->ybase - s->y);
+              if (s->y + s->height < s->ybase + position + thickness)
+                thickness = (s->y + s->height) - (s->ybase + position);
+              s->underline_thickness = thickness;
+              s->underline_position = position;
+              y = s->ybase + position;
+              if (s->face->underline_defaulted_p)
+                pgtk_fill_rectangle (s->f, s->xgcv.foreground,
+                                      s->x, y, s->width, thickness);
+              else
+                {
+                  pgtk_fill_rectangle (s->f, s->face->underline_color,
+                                        s->x, y, s->width, thickness);
+                }
+            }
+        }
+      /* Draw overline.  */
+      if (s->face->overline_p)
+       {
+         unsigned long dy = 0, h = 1;
+
+         if (s->face->overline_color_defaulted_p)
+           pgtk_fill_rectangle (s->f, s->xgcv.foreground, s->x, s->y + dy,
+                                  s->width, h);
+         else
+           {
+             pgtk_fill_rectangle (s->f, s->face->overline_color, s->x, s->y + 
dy,
+                                    s->width, h);
+           }
+       }
+
+      /* Draw strike-through.  */
+      if (s->face->strike_through_p)
+       {
+         /* Y-coordinate and height of the glyph string's first
+            glyph.  We cannot use s->y and s->height because those
+            could be larger if there are taller display elements
+            (e.g., characters displayed with a larger font) in the
+            same glyph row.  */
+         int glyph_y = s->ybase - s->first_glyph->ascent;
+         int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
+         /* Strike-through width and offset from the glyph string's
+            top edge.  */
+          unsigned long h = 1;
+          unsigned long dy = (glyph_height - h) / 2;
+
+         if (s->face->strike_through_color_defaulted_p)
+           pgtk_fill_rectangle (s->f, s->xgcv.foreground, s->x, glyph_y + dy,
+                                  s->width, h);
+         else
+           {
+             pgtk_fill_rectangle (s->f, s->face->strike_through_color, s->x, 
glyph_y + dy,
+                                    s->width, h);
+           }
+       }
+
+      /* Draw relief if not yet drawn.  */
+      if (!relief_drawn_p && s->face->box != FACE_NO_BOX)
+       x_draw_glyph_string_box (s);
+
+      if (s->prev)
+       {
+         struct glyph_string *prev;
+
+         for (prev = s->prev; prev; prev = prev->prev)
+           if (prev->hl != s->hl
+               && prev->x + prev->width + prev->right_overhang > s->x)
+             {
+               /* As prev was drawn while clipped to its own area, we
+                  must draw the right_overhang part using s->hl now.  */
+               enum draw_glyphs_face save = prev->hl;
+
+               prev->hl = s->hl;
+               PGTK_TRACE("pgtk_draw_glyph_string: 3.");
+               x_set_glyph_string_gc (prev);
+               cairo_save(cr);
+               x_set_glyph_string_clipping_exactly (s, prev, cr);
+               if (prev->first_glyph->type == CHAR_GLYPH)
+                 x_draw_glyph_string_foreground (prev);
+               else
+                 x_draw_composite_glyph_string_foreground (prev);
+               prev->hl = save;
+               prev->num_clips = 0;
+               cairo_restore(cr);
+             }
+       }
+
+      if (s->next)
+       {
+         struct glyph_string *next;
+
+         for (next = s->next; next; next = next->next)
+           if (next->hl != s->hl
+               && next->x - next->left_overhang < s->x + s->width)
+             {
+               /* As next will be drawn while clipped to its own area,
+                  we must draw the left_overhang part using s->hl now.  */
+               enum draw_glyphs_face save = next->hl;
+
+               next->hl = s->hl;
+               PGTK_TRACE("pgtk_draw_glyph_string: 4.");
+               x_set_glyph_string_gc (next);
+               cairo_save(cr);
+               x_set_glyph_string_clipping_exactly (s, next, cr);
+               if (next->first_glyph->type == CHAR_GLYPH)
+                 x_draw_glyph_string_foreground (next);
+               else
+                 x_draw_composite_glyph_string_foreground (next);
+               cairo_restore(cr);
+               next->hl = save;
+               next->num_clips = 0;
+               next->clip_head = s->next;
+             }
+       }
+    }
+
+  /* Reset clipping.  */
+  pgtk_end_cr_clip(s->f);
+  s->num_clips = 0;
+}
+
+/* RIF: Define cursor CURSOR on frame F.  */
+
+static void
+pgtk_define_frame_cursor (struct frame *f, Emacs_Cursor cursor)
+{
+  if (!f->pointer_invisible
+      && FRAME_X_OUTPUT(f)->current_cursor != cursor)
+    gdk_window_set_cursor(gtk_widget_get_window(FRAME_GTK_WIDGET(f)), cursor);
+  FRAME_X_OUTPUT(f)->current_cursor = cursor;
+}
+
+static void pgtk_after_update_window_line(struct window *w, struct glyph_row 
*desired_row)
+{
+  PGTK_TRACE("after_update_window_line.");
+
+  struct frame *f;
+  int width, height;
+
+  /* begin copy from other terms */
+  eassert (w);
+
+  if (!desired_row->mode_line_p && !w->pseudo_window_p)
+    desired_row->redraw_fringe_bitmaps_p = 1;
+
+  /* When a window has disappeared, make sure that no rest of
+     full-width rows stays visible in the internal border.  */
+  if (windows_or_buffers_changed
+      && desired_row->full_width_p
+      && (f = XFRAME (w->frame),
+         width = FRAME_INTERNAL_BORDER_WIDTH (f),
+         width != 0)
+      && (height = desired_row->visible_height,
+         height > 0))
+    {
+      int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y));
+
+      block_input ();
+      pgtk_clear_frame_area (f, 0, y, width, height);
+      pgtk_clear_frame_area (f,
+                              FRAME_PIXEL_WIDTH (f) - width,
+                              y, width, height);
+      unblock_input ();
+    }
+}
+
+static void pgtk_clear_frame_area(struct frame *f, int x, int y, int width, 
int height)
+{
+  PGTK_TRACE("clear_frame_area.");
+  pgtk_clear_area (f, x, y, width, height);
+}
+
+/* Draw a hollow box cursor on window W in glyph row ROW.  */
+
+static void
+x_draw_hollow_cursor (struct window *w, struct glyph_row *row)
+{
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+  int x, y, wd, h;
+  struct glyph *cursor_glyph;
+
+  /* Get the glyph the cursor is on.  If we can't tell because
+     the current matrix is invalid or such, give up.  */
+  cursor_glyph = get_phys_cursor_glyph (w);
+  if (cursor_glyph == NULL)
+    return;
+
+  /* Compute frame-relative coordinates for phys cursor.  */
+  get_phys_cursor_geometry (w, row, cursor_glyph, &x, &y, &h);
+  wd = w->phys_cursor_width - 1;
+
+  /* The foreground of cursor_gc is typically the same as the normal
+     background color, which can cause the cursor box to be invisible.  */
+  cairo_t *cr = pgtk_begin_cr_clip(f);
+  pgtk_set_cr_source_with_color(f, FRAME_X_OUTPUT(f)->cursor_color);
+
+  /* When on R2L character, show cursor at the right edge of the
+     glyph, unless the cursor box is as wide as the glyph or wider
+     (the latter happens when x-stretch-cursor is non-nil).  */
+  if ((cursor_glyph->resolved_level & 1) != 0
+      && cursor_glyph->pixel_width > wd)
+    {
+      x += cursor_glyph->pixel_width - wd;
+      if (wd > 0)
+       wd -= 1;
+    }
+  /* Set clipping, draw the rectangle, and reset clipping again.  */
+  pgtk_clip_to_row (w, row, TEXT_AREA, cr);
+  pgtk_draw_rectangle (f, FRAME_X_OUTPUT(f)->cursor_color, x, y, wd, h - 1);
+  pgtk_end_cr_clip(f);
+}
+
+/* Draw a bar cursor on window W in glyph row ROW.
+
+   Implementation note: One would like to draw a bar cursor with an
+   angle equal to the one given by the font property XA_ITALIC_ANGLE.
+   Unfortunately, I didn't find a font yet that has this property set.
+   --gerd.  */
+
+static void
+x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum 
text_cursor_kinds kind)
+{
+  struct frame *f = XFRAME (w->frame);
+  struct glyph *cursor_glyph;
+
+  /* If cursor is out of bounds, don't draw garbage.  This can happen
+     in mini-buffer windows when switching between echo area glyphs
+     and mini-buffer.  */
+  cursor_glyph = get_phys_cursor_glyph (w);
+  if (cursor_glyph == NULL)
+    return;
+
+  /* Experimental avoidance of cursor on xwidget.  */
+  if (cursor_glyph->type == XWIDGET_GLYPH)
+    return;
+
+  /* If on an image, draw like a normal cursor.  That's usually better
+     visible than drawing a bar, esp. if the image is large so that
+     the bar might not be in the window.  */
+  if (cursor_glyph->type == IMAGE_GLYPH)
+    {
+      struct glyph_row *r;
+      r = MATRIX_ROW (w->current_matrix, w->phys_cursor.vpos);
+      draw_phys_cursor_glyph (w, r, DRAW_CURSOR);
+    }
+  else
+    {
+      struct face *face = FACE_FROM_ID (f, cursor_glyph->face_id);
+      unsigned long color;
+
+      cairo_t *cr = pgtk_begin_cr_clip(f);
+
+      /* If the glyph's background equals the color we normally draw
+        the bars cursor in, the bar cursor in its normal color is
+        invisible.  Use the glyph's foreground color instead in this
+        case, on the assumption that the glyph's colors are chosen so
+        that the glyph is legible.  */
+      if (face->background == FRAME_X_OUTPUT(f)->cursor_color)
+       color = face->foreground;
+      else
+       color = FRAME_X_OUTPUT(f)->cursor_color;
+
+      pgtk_clip_to_row (w, row, TEXT_AREA, cr);
+
+      if (kind == BAR_CURSOR)
+       {
+         int x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x);
+
+         if (width < 0)
+           width = FRAME_CURSOR_WIDTH (f);
+         width = min (cursor_glyph->pixel_width, width);
+
+         w->phys_cursor_width = width;
+
+         /* If the character under cursor is R2L, draw the bar cursor
+            on the right of its glyph, rather than on the left.  */
+         if ((cursor_glyph->resolved_level & 1) != 0)
+           x += cursor_glyph->pixel_width - width;
+
+         pgtk_fill_rectangle (f, color, x,
+                                WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y),
+                                width, row->height);
+       }
+      else /* HBAR_CURSOR */
+       {
+         int dummy_x, dummy_y, dummy_h;
+         int x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x);
+
+         if (width < 0)
+           width = row->height;
+
+         width = min (row->height, width);
+
+         get_phys_cursor_geometry (w, row, cursor_glyph, &dummy_x,
+                                   &dummy_y, &dummy_h);
+
+         if ((cursor_glyph->resolved_level & 1) != 0
+             && cursor_glyph->pixel_width > w->phys_cursor_width - 1)
+           x += cursor_glyph->pixel_width - w->phys_cursor_width + 1;
+         pgtk_fill_rectangle (f, color, x,
+                                WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y +
+                                                         row->height - width),
+                                w->phys_cursor_width - 1, width);
+       }
+
+      pgtk_end_cr_clip(f);
+    }
+}
+
+/* RIF: Draw cursor on window W.  */
+
+static void
+pgtk_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, int x,
+                     int y, enum text_cursor_kinds cursor_type,
+                     int cursor_width, bool on_p, bool active_p)
+{
+  PGTK_TRACE("draw_window_cursor: %d, %d, %d, %d, %d, %d.",
+              x, y, cursor_type, cursor_width, on_p, active_p);
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+
+  if (on_p)
+    {
+      w->phys_cursor_type = cursor_type;
+      w->phys_cursor_on_p = true;
+
+      if (glyph_row->exact_window_width_line_p
+         && (glyph_row->reversed_p
+             ? (w->phys_cursor.hpos < 0)
+             : (w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])))
+       {
+         glyph_row->cursor_in_fringe_p = true;
+         draw_fringe_bitmap (w, glyph_row, glyph_row->reversed_p);
+       }
+      else
+       {
+         switch (cursor_type)
+           {
+           case HOLLOW_BOX_CURSOR:
+             x_draw_hollow_cursor (w, glyph_row);
+             break;
+
+           case FILLED_BOX_CURSOR:
+             draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
+             break;
+
+           case BAR_CURSOR:
+             x_draw_bar_cursor (w, glyph_row, cursor_width, BAR_CURSOR);
+             break;
+
+           case HBAR_CURSOR:
+             x_draw_bar_cursor (w, glyph_row, cursor_width, HBAR_CURSOR);
+             break;
+
+           case NO_CURSOR:
+             w->phys_cursor_width = 0;
+             break;
+
+           default:
+             emacs_abort ();
+           }
+       }
+
+#ifdef HAVE_X_I18N
+      if (w == XWINDOW (f->selected_window))
+       if (FRAME_XIC (f) && (FRAME_XIC_STYLE (f) & XIMPreeditPosition))
+         xic_set_preeditarea (w, x, y);
+#endif
+    }
+
+  gtk_widget_queue_draw(FRAME_GTK_WIDGET(f));
+}
+
+static void
+pgtk_copy_bits (struct frame *f, cairo_rectangle_t *src_rect, 
cairo_rectangle_t *dst_rect)
+{
+  PGTK_TRACE ("pgtk_copy_bits: %dx%d+%d+%d -> %dx%d+%d+%d",
+             (int) src_rect->width,
+             (int) src_rect->height,
+             (int) src_rect->x,
+             (int) src_rect->y,
+             (int) dst_rect->width,
+             (int) dst_rect->height,
+             (int) dst_rect->x,
+             (int) dst_rect->y);
+
+  cairo_t *cr;
+  cairo_surface_t *surface;  /* temporary surface */
+
+  surface = cairo_surface_create_similar(FRAME_CR_SURFACE(f), 
CAIRO_CONTENT_COLOR_ALPHA,
+                                        (int) src_rect->width,
+                                        (int) src_rect->height);
+
+  cr = cairo_create(surface);
+  cairo_set_source_surface(cr, FRAME_CR_SURFACE(f), -src_rect->x, 
-src_rect->y);
+  cairo_rectangle(cr, 0, 0, src_rect->width, src_rect->height);
+  cairo_clip(cr);
+  cairo_paint(cr);
+  cairo_destroy(cr);
+
+  cr = pgtk_begin_cr_clip(f);
+  cairo_set_source_surface(cr, surface, dst_rect->x, dst_rect->y);
+  cairo_rectangle(cr, dst_rect->x, dst_rect->y, dst_rect->width, 
dst_rect->height);
+  cairo_clip(cr);
+  cairo_paint(cr);
+  pgtk_end_cr_clip(f);
+
+  cairo_surface_destroy(surface);
+}
+
+/* Scroll part of the display as described by RUN.  */
+
+static void
+pgtk_scroll_run (struct window *w, struct run *run)
+{
+  struct frame *f = XFRAME (w->frame);
+  int x, y, width, height, from_y, to_y, bottom_y;
+
+  /* Get frame-relative bounding box of the text display area of W,
+     without mode lines.  Include in this box the left and right
+     fringe of W.  */
+  window_box (w, ANY_AREA, &x, &y, &width, &height);
+
+  from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
+  to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
+  bottom_y = y + height;
+
+  if (to_y < from_y)
+    {
+      /* Scrolling up.  Make sure we don't copy part of the mode
+        line at the bottom.  */
+      if (from_y + run->height > bottom_y)
+       height = bottom_y - from_y;
+      else
+       height = run->height;
+    }
+  else
+    {
+      /* Scrolling down.  Make sure we don't copy over the mode line.
+        at the bottom.  */
+      if (to_y + run->height > bottom_y)
+       height = bottom_y - to_y;
+      else
+       height = run->height;
+    }
+
+  block_input ();
+
+  /* Cursor off.  Will be switched on again in x_update_window_end.  */
+  x_clear_cursor (w);
+
+  {
+    cairo_rectangle_t src_rect = { x, from_y, width, height };
+    cairo_rectangle_t dst_rect = { x, to_y, width, height };
+    pgtk_copy_bits (f, &src_rect , &dst_rect);
+  }
+
+  unblock_input ();
+}
+
+/***********************************************************************
+                   Starting and ending an update
+ ***********************************************************************/
+
+/* Start an update of frame F.  This function is installed as a hook
+   for update_begin, i.e. it is called when update_begin is called.
+   This function is called prior to calls to x_update_window_begin for
+   each window being updated.  Currently, there is nothing to do here
+   because all interesting stuff is done on a window basis.  */
+
+static void
+pgtk_update_begin (struct frame *f)
+{
+  if (! NILP (tip_frame) && XFRAME (tip_frame) == f
+      && ! FRAME_VISIBLE_P (f))
+    return;
+
+  if (! FRAME_CR_SURFACE (f))
+    {
+      int width = FRAME_PIXEL_WIDTH (f);
+      int height = FRAME_PIXEL_HEIGHT (f);
+
+      if (width > 0 && height > 0)
+        {
+          block_input();
+          FRAME_CR_SURFACE (f) = cairo_image_surface_create
+            (CAIRO_FORMAT_ARGB32, width, height);
+          unblock_input();
+        }
+    }
+
+  pgtk_clear_under_internal_border (f);
+}
+
+/* Start update of window W.  */
+
+static void
+pgtk_update_window_begin (struct window *w)
+{
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+  Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);
+
+  w->output_cursor = w->cursor;
+
+  block_input ();
+
+  if (f == hlinfo->mouse_face_mouse_frame)
+    {
+      /* Don't do highlighting for mouse motion during the update.  */
+      hlinfo->mouse_face_defer = true;
+
+      /* If F needs to be redrawn, simply forget about any prior mouse
+        highlighting.  */
+      if (FRAME_GARBAGED_P (f))
+       hlinfo->mouse_face_window = Qnil;
+    }
+
+  unblock_input ();
+}
+
+
+/* Draw a vertical window border from (x,y0) to (x,y1)  */
+
+static void
+pgtk_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
+{
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+  struct face *face;
+  cairo_t *cr;
+
+  cr = pgtk_begin_cr_clip (f);
+
+  face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
+  if (face)
+    pgtk_set_cr_source_with_color (f, face->foreground);
+
+  cairo_rectangle (cr, x, y0, 1, y1 - y0);
+  cairo_fill (cr);
+
+  pgtk_end_cr_clip (f);
+}
+
+/* Draw a window divider from (x0,y0) to (x1,y1)  */
+
+static void
+pgtk_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
+{
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+  struct face *face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
+  struct face *face_first
+    = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID);
+  struct face *face_last
+    = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
+  unsigned long color = face ? face->foreground : FRAME_FOREGROUND_PIXEL (f);
+  unsigned long color_first = (face_first
+                              ? face_first->foreground
+                              : FRAME_FOREGROUND_PIXEL (f));
+  unsigned long color_last = (face_last
+                             ? face_last->foreground
+                             : FRAME_FOREGROUND_PIXEL (f));
+  cairo_t *cr = pgtk_begin_cr_clip (f);
+
+  if (y1 - y0 > x1 - x0 && x1 - x0 > 2)
+    /* Vertical.  */
+    {
+      pgtk_set_cr_source_with_color (f, color_first);
+      cairo_rectangle (cr, x0, y0, 1, y1 - y0);
+      cairo_fill(cr);
+      pgtk_set_cr_source_with_color (f, color);
+      cairo_rectangle (cr, x0 + 1, y0, x1 - x0 - 2, y1 - y0);
+      cairo_fill(cr);
+      pgtk_set_cr_source_with_color (f, color_last);
+      cairo_rectangle (cr, x1 - 1, y0, 1, y1 - y0);
+      cairo_fill(cr);
+    }
+  else if (x1 - x0 > y1 - y0 && y1 - y0 > 3)
+    /* Horizontal.  */
+    {
+      pgtk_set_cr_source_with_color (f, color_first);
+      cairo_rectangle (cr, x0, y0, x1 - x0, 1);
+      cairo_fill(cr);
+      pgtk_set_cr_source_with_color (f, color);
+      cairo_rectangle (cr, x0, y0 + 1, x1 - x0, y1 - y0 - 2);
+      cairo_fill(cr);
+      pgtk_set_cr_source_with_color (f, color_last);
+      cairo_rectangle (cr, x0, y1 - 1, x1 - x0, 1);
+      cairo_fill(cr);
+    }
+  else
+    {
+      pgtk_set_cr_source_with_color (f, color);
+      cairo_rectangle (cr, x0, y0, x1 - x0, y1 - y0);
+      cairo_fill(cr);
+    }
+
+  pgtk_end_cr_clip (f);
+}
+
+/* End update of window W.
+
+   Draw vertical borders between horizontally adjacent windows, and
+   display W's cursor if CURSOR_ON_P is non-zero.
+
+   MOUSE_FACE_OVERWRITTEN_P non-zero means that some row containing
+   glyphs in mouse-face were overwritten.  In that case we have to
+   make sure that the mouse-highlight is properly redrawn.
+
+   W may be a menu bar pseudo-window in case we don't have X toolkit
+   support.  Such windows don't have a cursor, so don't display it
+   here.  */
+
+static void
+pgtk_update_window_end (struct window *w, bool cursor_on_p,
+                    bool mouse_face_overwritten_p)
+{
+  if (!w->pseudo_window_p)
+    {
+      block_input ();
+
+      if (cursor_on_p)
+       display_and_set_cursor (w, true,
+                               w->output_cursor.hpos, w->output_cursor.vpos,
+                               w->output_cursor.x, w->output_cursor.y);
+
+      if (draw_window_fringes (w, true))
+       {
+         if (WINDOW_RIGHT_DIVIDER_WIDTH (w))
+           x_draw_right_divider (w);
+         else
+           x_draw_vertical_border (w);
+       }
+
+      unblock_input ();
+    }
+
+  /* If a row with mouse-face was overwritten, arrange for
+     XTframe_up_to_date to redisplay the mouse highlight.  */
+  if (mouse_face_overwritten_p)
+    {
+      Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (XFRAME (w->frame));
+
+      hlinfo->mouse_face_beg_row = hlinfo->mouse_face_beg_col = -1;
+      hlinfo->mouse_face_end_row = hlinfo->mouse_face_end_col = -1;
+      hlinfo->mouse_face_window = Qnil;
+    }
+}
+
+/* End update of frame F.  This function is installed as a hook in
+   update_end.  */
+
+static void
+pgtk_update_end (struct frame *f)
+{
+  /* Mouse highlight may be displayed again.  */
+  MOUSE_HL_INFO (f)->mouse_face_defer = false;
+}
+
+/* Return the current position of the mouse.
+   *FP should be a frame which indicates which display to ask about.
+
+   If the mouse movement started in a scroll bar, set *FP, *BAR_WINDOW,
+   and *PART to the frame, window, and scroll bar part that the mouse
+   is over.  Set *X and *Y to the portion and whole of the mouse's
+   position on the scroll bar.
+
+   If the mouse movement started elsewhere, set *FP to the frame the
+   mouse is on, *BAR_WINDOW to nil, and *X and *Y to the character cell
+   the mouse is over.
+
+   Set *TIMESTAMP to the server time-stamp for the time at which the mouse
+   was at this position.
+
+   Don't store anything if we don't have a valid set of values to report.
+
+   This clears the mouse_moved flag, so we can wait for the next mouse
+   movement.  */
+
+static void
+pgtk_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
+                    enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
+                    Time *timestamp)
+{
+  struct frame *f1;
+  struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (*fp);
+  int win_x, win_y;
+  GdkSeat *seat;
+  GdkDevice *device;
+
+  block_input ();
+
+  Lisp_Object frame, tail;
+
+  /* Clear the mouse-moved flag for every frame on this display.  */
+  FOR_EACH_FRAME (tail, frame)
+    if (FRAME_PGTK_P (XFRAME (frame))
+       && FRAME_X_DISPLAY (XFRAME (frame)) == FRAME_X_DISPLAY (*fp))
+      XFRAME (frame)->mouse_moved = false;
+
+  dpyinfo->last_mouse_scroll_bar = NULL;
+
+  seat = gdk_display_get_default_seat(dpyinfo->gdpy);
+  device = gdk_seat_get_pointer(seat);
+
+  if (x_mouse_grabbed (dpyinfo)) {
+    GdkWindow *win;
+    GdkModifierType mask;
+    /* get x, y relative to edit window of f1. */
+    f1 = dpyinfo->last_mouse_frame;
+    win = gtk_widget_get_window(FRAME_GTK_WIDGET(f1));
+    win = gdk_window_get_device_position(win, device, &win_x, &win_y, &mask);
+  } else {
+    GdkWindow *win;
+    GdkModifierType mask;
+    /* 1. get frame where the pointer is on. */
+    win = gtk_widget_get_window(FRAME_GTK_WIDGET(*fp));
+    win = gdk_window_get_device_position(win, device, &win_x, &win_y, &mask);
+    if (win != NULL)
+      f1 = pgtk_any_window_to_frame(win);
+    else
+      f1 = SELECTED_FRAME();
+
+    /* 2. get x, y relative to edit window of the frame. */
+    win = gtk_widget_get_window(FRAME_GTK_WIDGET(f1));
+    win = gdk_window_get_device_position(win, device, &win_x, &win_y, &mask);
+  }
+
+  if (f1 != NULL) {
+    dpyinfo = FRAME_DISPLAY_INFO (f1);
+    remember_mouse_glyph (f1, win_x, win_y, &dpyinfo->last_mouse_glyph);
+    dpyinfo->last_mouse_glyph_frame = f1;
+
+    *bar_window = Qnil;
+    *part = 0;
+    *fp = f1;
+    XSETINT (*x, win_x);
+    XSETINT (*y, win_y);
+    *timestamp = dpyinfo->last_mouse_movement_time;
+  }
+
+  unblock_input ();
+}
+
+/* Fringe bitmaps.  */
+
+static int max_fringe_bmp = 0;
+static cairo_pattern_t **fringe_bmp = 0;
+
+static void
+pgtk_define_fringe_bitmap (int which, unsigned short *bits, int h, int wd)
+{
+  int i, stride;
+  cairo_surface_t *surface;
+  unsigned char *data;
+  cairo_pattern_t *pattern;
+
+  if (which >= max_fringe_bmp)
+    {
+      i = max_fringe_bmp;
+      max_fringe_bmp = which + 20;
+      fringe_bmp = (cairo_pattern_t **) xrealloc (fringe_bmp, max_fringe_bmp * 
sizeof (cairo_pattern_t *));
+      while (i < max_fringe_bmp)
+       fringe_bmp[i++] = 0;
+    }
+
+  block_input ();
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_A1, wd, h);
+  stride = cairo_image_surface_get_stride (surface);
+  data = cairo_image_surface_get_data (surface);
+
+  for (i = 0; i < h; i++)
+    {
+      *((unsigned short *) data) = bits[i];
+      data += stride;
+    }
+
+  cairo_surface_mark_dirty (surface);
+  pattern = cairo_pattern_create_for_surface (surface);
+  cairo_surface_destroy (surface);
+
+  unblock_input ();
+
+  fringe_bmp[which] = pattern;
+}
+
+static void
+pgtk_destroy_fringe_bitmap (int which)
+{
+  if (which >= max_fringe_bmp)
+    return;
+
+  if (fringe_bmp[which])
+    {
+      block_input ();
+      cairo_pattern_destroy (fringe_bmp[which]);
+      unblock_input ();
+    }
+  fringe_bmp[which] = 0;
+}
+
+static void
+pgtk_clip_to_row (struct window *w, struct glyph_row *row,
+                   enum glyph_row_area area, cairo_t *cr)
+{
+  int window_x, window_y, window_width;
+  cairo_rectangle_int_t rect;
+
+  window_box (w, area, &window_x, &window_y, &window_width, 0);
+
+  rect.x = window_x;
+  rect.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
+  rect.y = max (rect.y, window_y);
+  rect.width = window_width;
+  rect.height = row->visible_height;
+
+  cairo_rectangle(cr, rect.x, rect.y, rect.width, rect.height);
+  cairo_clip(cr);
+}
+
+static void
+pgtk_cr_draw_image (struct frame *f, GC gc, cairo_pattern_t *image,
+                int src_x, int src_y, int width, int height,
+                int dest_x, int dest_y, bool overlay_p)
+{
+  cairo_t *cr;
+  cairo_matrix_t matrix;
+  cairo_surface_t *surface;
+  cairo_format_t format;
+
+  PGTK_TRACE("pgtk_cr_draw_image: 0: %d,%d,%d,%d,%d,%d,%d.", src_x, src_y, 
width, height, dest_x, dest_y, overlay_p);
+  cr = pgtk_begin_cr_clip (f);
+  if (overlay_p)
+    cairo_rectangle (cr, dest_x, dest_y, width, height);
+  else
+    {
+      pgtk_set_cr_source_with_gc_background (f, gc);
+      cairo_rectangle (cr, dest_x, dest_y, width, height);
+      cairo_fill_preserve (cr);
+    }
+  cairo_clip (cr);
+  cairo_matrix_init_translate (&matrix, src_x - dest_x, src_y - dest_y);
+  cairo_pattern_set_matrix (image, &matrix);
+  cairo_pattern_get_surface (image, &surface);
+  format = cairo_image_surface_get_format (surface);
+  if (format != CAIRO_FORMAT_A8 && format != CAIRO_FORMAT_A1)
+    {
+      PGTK_TRACE("other format.");
+      cairo_set_source (cr, image);
+      cairo_fill (cr);
+    }
+  else
+    {
+      if (format == CAIRO_FORMAT_A8)
+       PGTK_TRACE("format A8.");
+      else if (format == CAIRO_FORMAT_A1)
+       PGTK_TRACE("format A1.");
+      else
+       PGTK_TRACE("format ??.");
+      pgtk_set_cr_source_with_gc_foreground (f, gc);
+      cairo_rectangle_list_t *rects = cairo_copy_clip_rectangle_list(cr);
+      PGTK_TRACE("rects:");
+      PGTK_TRACE(" status: %u", rects->status);
+      PGTK_TRACE(" rectangles:");
+      for (int i = 0; i < rects->num_rectangles; i++) {
+       PGTK_TRACE("  %fx%f+%f+%f",
+               rects->rectangles[i].width,
+               rects->rectangles[i].height,
+               rects->rectangles[i].x,
+               rects->rectangles[i].y);
+      }
+      cairo_rectangle_list_destroy(rects);
+      cairo_mask (cr, image);
+    }
+  pgtk_end_cr_clip (f);
+  PGTK_TRACE("pgtk_cr_draw_image: 9.");
+}
+
+static void
+pgtk_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct 
draw_fringe_bitmap_params *p)
+{
+  PGTK_TRACE("draw_fringe_bitmap.");
+
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+  struct face *face = p->face;
+
+  cairo_t *cr = pgtk_begin_cr_clip(f);
+  cairo_save(cr);
+
+  /* Must clip because of partially visible lines.  */
+  pgtk_clip_to_row (w, row, ANY_AREA, cr);
+
+  if (p->bx >= 0 && !p->overlay_p)
+    {
+      /* In case the same realized face is used for fringes and
+        for something displayed in the text (e.g. face `region' on
+        mono-displays, the fill style may have been changed to
+        FillSolid in x_draw_glyph_string_background.  */
+#if 0
+      if (face->stipple)
+       XSetFillStyle (display, face->gc, FillOpaqueStippled);
+      else
+#endif
+       pgtk_set_cr_source_with_color(f, face->background);
+
+      cairo_rectangle(cr, p->bx, p->by, p->nx, p->ny);
+      cairo_fill(cr);
+    }
+
+  PGTK_TRACE("which: %d, max_fringe_bmp: %d.", p->which, max_fringe_bmp);
+  if (p->which && p->which < max_fringe_bmp)
+    {
+      XGCValues gcv;
+
+      PGTK_TRACE("cursor_p=%d.", p->cursor_p);
+      PGTK_TRACE("overlay_p_p=%d.", p->overlay_p);
+      PGTK_TRACE("background=%08lx.", face->background);
+      PGTK_TRACE("cursor_color=%08lx.", FRAME_X_OUTPUT(f)->cursor_color);
+      PGTK_TRACE("foreground=%08lx.", face->foreground);
+      gcv.foreground = (p->cursor_p
+                      ? (p->overlay_p ? face->background
+                         : FRAME_X_OUTPUT(f)->cursor_color)
+                      : face->foreground);
+      gcv.background = face->background;
+      pgtk_cr_draw_image (f, &gcv, fringe_bmp[p->which], 0, p->dh,
+                      p->wd, p->h, p->x, p->y, p->overlay_p);
+    }
+
+  cairo_restore(cr);
+}
+
+static struct atimer *hourglass_atimer = NULL;
+static int hourglass_enter_count = 0;
+
+static void hourglass_cb(struct atimer *timer)
+{
+  /*NOP*/
+}
+
+static void
+pgtk_show_hourglass(struct frame *f)
+{
+  struct pgtk_output *x = FRAME_X_OUTPUT(f);
+  if (x->hourglass_widget != NULL)
+    gtk_widget_destroy(x->hourglass_widget);
+  x->hourglass_widget = gtk_event_box_new();   /* gtk_event_box is 
GDK_INPUT_ONLY. */
+  gtk_widget_set_has_window(x->hourglass_widget, true);
+  gtk_fixed_put(GTK_FIXED(FRAME_GTK_WIDGET(f)), x->hourglass_widget, 0, 0);
+  gtk_widget_show(x->hourglass_widget);
+  gtk_widget_set_size_request(x->hourglass_widget, 30000, 30000);
+  gdk_window_raise(gtk_widget_get_window(x->hourglass_widget));
+  gdk_window_set_cursor(gtk_widget_get_window(x->hourglass_widget), 
x->hourglass_cursor);
+
+  /* For cursor animation, we receive signals, set pending_signals, and 
dispatch. */
+  if (hourglass_enter_count++ == 0) {
+    struct timespec ts = make_timespec(0, 50 * 1000 * 1000);
+    if (hourglass_atimer != NULL)
+      cancel_atimer(hourglass_atimer);
+    hourglass_atimer = start_atimer(ATIMER_CONTINUOUS, ts, hourglass_cb, NULL);
+  }
+
+  /* Cursor frequently stops animation. gtk's bug? */
+}
+
+static void
+pgtk_hide_hourglass(struct frame *f)
+{
+  struct pgtk_output *x = FRAME_X_OUTPUT(f);
+  if (--hourglass_enter_count == 0) {
+    if (hourglass_atimer != NULL) {
+      cancel_atimer(hourglass_atimer);
+      hourglass_atimer = NULL;
+    }
+  }
+  if (x->hourglass_widget != NULL) {
+    gtk_widget_destroy(x->hourglass_widget);
+    x->hourglass_widget = NULL;
+  }
+}
+
+/* Flushes changes to display.  */
+static void
+pgtk_flush_display (struct frame *f)
+{
+  block_input ();
+  gdk_flush();
+  unblock_input ();
+}
+
+extern frame_parm_handler pgtk_frame_parm_handlers[];
+
+static struct redisplay_interface pgtk_redisplay_interface =
+{
+  pgtk_frame_parm_handlers,
+  x_produce_glyphs,
+  x_write_glyphs,
+  x_insert_glyphs,
+  x_clear_end_of_line,
+  pgtk_scroll_run,
+  pgtk_after_update_window_line,
+  pgtk_update_window_begin,
+  pgtk_update_window_end,
+  pgtk_flush_display,
+  x_clear_window_mouse_face,
+  x_get_glyph_overhangs,
+  x_fix_overlapping_area,
+  pgtk_draw_fringe_bitmap,
+  pgtk_define_fringe_bitmap,
+  pgtk_destroy_fringe_bitmap,
+  pgtk_compute_glyph_string_overhangs,
+  pgtk_draw_glyph_string,
+  pgtk_define_frame_cursor,
+  pgtk_clear_frame_area,
+  pgtk_draw_window_cursor,
+  pgtk_draw_vertical_window_border,
+  pgtk_draw_window_divider,
+  NULL, // pgtk_shift_glyphs_for_insert,
+  pgtk_show_hourglass,
+  pgtk_hide_hourglass
+};
+
+static void
+pgtk_redraw_scroll_bars (struct frame *f)
+{
+  PGTK_TRACE("pgtk_redraw_scroll_bars");
+}
+
+void
+pgtk_clear_frame (struct frame *f)
+/* --------------------------------------------------------------------------
+      External (hook): Erase the entire frame
+   -------------------------------------------------------------------------- 
*/
+{
+  PGTK_TRACE("pgtk_clear_frame");
+ /* comes on initial frame because we have
+    after-make-frame-functions = select-frame */
+  if (!FRAME_DEFAULT_FACE (f))
+    return;
+
+  // mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
+
+  block_input ();
+
+  pgtk_clear_area(f, 0, 0, FRAME_PIXEL_WIDTH(f), FRAME_PIXEL_HEIGHT(f));
+
+  /* as of 2006/11 or so this is now needed */
+  pgtk_redraw_scroll_bars (f);
+  unblock_input ();
+}
+
+/* Invert the middle quarter of the frame for .15 sec.  */
+
+static void
+recover_from_visible_bell(struct atimer *timer)
+{
+  struct frame *f = timer->client_data;
+
+  if (FRAME_X_OUTPUT(f)->cr_surface_visible_bell != NULL) {
+    cairo_surface_destroy(FRAME_X_OUTPUT(f)->cr_surface_visible_bell);
+    FRAME_X_OUTPUT(f)->cr_surface_visible_bell = NULL;
+  }
+
+  if (FRAME_X_OUTPUT(f)->atimer_visible_bell != NULL)
+    FRAME_X_OUTPUT(f)->atimer_visible_bell = NULL;
+
+  gtk_widget_queue_draw(FRAME_GTK_WIDGET(f));
+}
+
+static void
+pgtk_flash (struct frame *f)
+{
+  block_input ();
+
+  {
+    cairo_surface_t *surface_orig = FRAME_CR_SURFACE(f);
+
+    int width = cairo_image_surface_get_width(surface_orig);
+    int height = cairo_image_surface_get_height(surface_orig);
+    cairo_surface_t *surface = cairo_surface_create_similar(surface_orig, 
CAIRO_CONTENT_COLOR_ALPHA,
+                                                           width, height);
+
+    cairo_t *cr = cairo_create(surface);
+    cairo_set_source_surface(cr, surface_orig, 0, 0);
+    cairo_rectangle(cr, 0, 0, width, height);
+    cairo_clip(cr);
+    cairo_paint(cr);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_set_operator (cr, CAIRO_OPERATOR_DIFFERENCE);
+
+#define XFillRectangle(d, win, gc, x, y, w, h) \
+    ( cairo_rectangle (cr, x, y, w, h), cairo_fill (cr) )
+
+    {
+      /* Get the height not including a menu bar widget.  */
+      int height = FRAME_PIXEL_HEIGHT (f);
+      /* Height of each line to flash.  */
+      int flash_height = FRAME_LINE_HEIGHT (f);
+      /* These will be the left and right margins of the rectangles.  */
+      int flash_left = FRAME_INTERNAL_BORDER_WIDTH (f);
+      int flash_right = FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH 
(f);
+      int width = flash_right - flash_left;
+
+      /* If window is tall, flash top and bottom line.  */
+      if (height > 3 * FRAME_LINE_HEIGHT (f))
+       {
+         XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), gc,
+                         flash_left,
+                         (FRAME_INTERNAL_BORDER_WIDTH (f)
+                          + FRAME_TOP_MARGIN_HEIGHT (f)),
+                         width, flash_height);
+         XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), gc,
+                         flash_left,
+                         (height - flash_height
+                          - FRAME_INTERNAL_BORDER_WIDTH (f)),
+                         width, flash_height);
+
+       }
+      else
+       /* If it is short, flash it all.  */
+       XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), gc,
+                       flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
+                       width, height - 2 * FRAME_INTERNAL_BORDER_WIDTH (f));
+
+      FRAME_X_OUTPUT(f)->cr_surface_visible_bell = surface;
+      gtk_widget_queue_draw(FRAME_GTK_WIDGET(f));
+
+      {
+       struct timespec delay = make_timespec (0, 50 * 1000 * 1000);
+       if (FRAME_X_OUTPUT(f)->atimer_visible_bell != NULL) {
+         cancel_atimer(FRAME_X_OUTPUT(f)->atimer_visible_bell);
+         FRAME_X_OUTPUT(f)->atimer_visible_bell = NULL;
+       }
+       FRAME_X_OUTPUT(f)->atimer_visible_bell = start_atimer(ATIMER_RELATIVE, 
delay, recover_from_visible_bell, f);
+      }
+
+#undef XFillRectangle
+    }
+  }
+
+  unblock_input ();
+}
+
+/* Make audible bell.  */
+
+static void
+pgtk_ring_bell (struct frame *f)
+{
+  if (visible_bell)
+    {
+      pgtk_flash(f);
+    }
+  else
+    {
+      block_input ();
+      gtk_widget_error_bell(FRAME_GTK_WIDGET(f));
+      unblock_input ();
+    }
+}
+
+/* Read events coming from the X server.
+   Return as soon as there are no more events to be read.
+
+   Return the number of characters stored into the buffer,
+   thus pretending to be `read' (except the characters we store
+   in the keyboard buffer can be multibyte, so are not necessarily
+   C chars).  */
+
+static int
+pgtk_read_socket (struct terminal *terminal, struct input_event *hold_quit)
+{
+  PGTK_TRACE("pgtk_read_socket: enter.");
+  GMainContext *context;
+  bool context_acquired = false;
+  int count;
+
+  count = evq_flush(hold_quit);
+  if (count > 0) {
+    PGTK_TRACE("pgtk_read_socket: leave(1).");
+    return count;
+  }
+
+  context = g_main_context_default ();
+  context_acquired = g_main_context_acquire (context);
+
+  block_input ();
+  PGTK_TRACE("pgtk_read_socket: 3: errno=%d.", errno);
+
+  if (context_acquired) {
+    PGTK_TRACE("pgtk_read_socket: 4.1: acquired.");
+    while (g_main_context_pending (context)) {
+      PGTK_TRACE("pgtk_read_socket: 4: dispatch...");
+      g_main_context_dispatch (context);
+      PGTK_TRACE("pgtk_read_socket: 5: dispatch... done.");
+    }
+  }
+
+  PGTK_TRACE("pgtk_read_socket: 7: errno=%d.", errno);
+  unblock_input ();
+
+  if (context_acquired)
+    g_main_context_release (context);
+
+  count = evq_flush(hold_quit);
+  if (count > 0) {
+    PGTK_TRACE("pgtk_read_socket: leave(2).");
+    return count;
+  }
+
+  PGTK_TRACE("pgtk_read_socket: leave(3).");
+  return 0;
+}
+
+int
+pgtk_select (int fds_lim, fd_set *rfds, fd_set *wfds, fd_set *efds,
+            struct timespec *timeout, sigset_t *sigmask)
+{
+  fd_set all_rfds, all_wfds;
+  struct timespec tmo;
+  struct timespec *tmop = timeout;
+
+  GMainContext *context;
+  bool have_wfds = wfds != NULL;
+  GPollFD gfds_buf[128];
+  GPollFD *gfds = gfds_buf;
+  int gfds_size = ARRAYELTS (gfds_buf);
+  int n_gfds, retval = 0, our_fds = 0, max_fds = fds_lim - 1;
+  bool context_acquired = false;
+  int i, nfds, tmo_in_millisec, must_free = 0;
+  bool need_to_dispatch;
+
+  PGTK_TRACE("pgtk_select: enter.");
+
+  if (event_q.nr >= 1) {
+    PGTK_TRACE("pgtk_select: raise.");
+    raise(SIGIO);
+    errno = EINTR;
+    return -1;
+  }
+
+  context = g_main_context_default ();
+  context_acquired = g_main_context_acquire (context);
+  /* FIXME: If we couldn't acquire the context, we just silently proceed
+     because this function handles more than just glib file descriptors.
+     Note that, as implemented, this failure is completely silent: there is
+     no feedback to the caller.  */
+
+  if (rfds) all_rfds = *rfds;
+  else FD_ZERO (&all_rfds);
+  if (wfds) all_wfds = *wfds;
+  else FD_ZERO (&all_wfds);
+
+  n_gfds = (context_acquired
+           ? g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
+                                   gfds, gfds_size)
+           : -1);
+
+  if (gfds_size < n_gfds)
+    {
+      /* Avoid using SAFE_NALLOCA, as that implicitly refers to the
+        current thread.  Using xnmalloc avoids thread-switching
+        problems here.  */
+      gfds = xnmalloc (n_gfds, sizeof *gfds);
+      must_free = 1;
+      gfds_size = n_gfds;
+      n_gfds = g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
+                                    gfds, gfds_size);
+    }
+
+  for (i = 0; i < n_gfds; ++i)
+    {
+      if (gfds[i].events & G_IO_IN)
+        {
+          FD_SET (gfds[i].fd, &all_rfds);
+          if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
+        }
+      if (gfds[i].events & G_IO_OUT)
+        {
+          FD_SET (gfds[i].fd, &all_wfds);
+          if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
+          have_wfds = true;
+        }
+    }
+
+  if (must_free)
+    xfree (gfds);
+
+  if (n_gfds >= 0 && tmo_in_millisec >= 0)
+    {
+      tmo = make_timespec (tmo_in_millisec / 1000,
+                          1000 * 1000 * (tmo_in_millisec % 1000));
+      if (!timeout || timespec_cmp (tmo, *timeout) < 0)
+       tmop = &tmo;
+    }
+
+  fds_lim = max_fds + 1;
+  nfds = thread_select (pselect, fds_lim,
+                       &all_rfds, have_wfds ? &all_wfds : NULL, efds,
+                       tmop, sigmask);
+  if (nfds < 0)
+    retval = nfds;
+  else if (nfds > 0)
+    {
+      for (i = 0; i < fds_lim; ++i)
+        {
+          if (FD_ISSET (i, &all_rfds))
+            {
+              if (rfds && FD_ISSET (i, rfds)) ++retval;
+              else ++our_fds;
+            }
+          else if (rfds)
+            FD_CLR (i, rfds);
+
+          if (have_wfds && FD_ISSET (i, &all_wfds))
+            {
+              if (wfds && FD_ISSET (i, wfds)) ++retval;
+              else ++our_fds;
+            }
+          else if (wfds)
+            FD_CLR (i, wfds);
+
+          if (efds && FD_ISSET (i, efds))
+            ++retval;
+        }
+    }
+
+  /* If Gtk+ is in use eventually gtk_main_iteration will be called,
+     unless retval is zero.  */
+  need_to_dispatch = retval == 0;
+  if (need_to_dispatch && context_acquired)
+    {
+      int pselect_errno = errno;
+      PGTK_TRACE("retval=%d.", retval);
+      PGTK_TRACE("need_to_dispatch=%d.", need_to_dispatch);
+      PGTK_TRACE("context_acquired=%d.", context_acquired);
+      PGTK_TRACE("pselect_errno=%d.", pselect_errno);
+      /* Prevent g_main_dispatch recursion, that would occur without
+         block_input wrapper, because event handlers call
+         unblock_input.  Event loop recursion was causing Bug#15801.  */
+      block_input ();
+      while (g_main_context_pending (context)) {
+       PGTK_TRACE("dispatch...");
+        g_main_context_dispatch (context);
+       PGTK_TRACE("dispatch... done.");
+      }
+      unblock_input ();
+      errno = pselect_errno;
+    }
+
+  if (context_acquired)
+    g_main_context_release (context);
+
+  /* To not have to recalculate timeout, return like this.  */
+  if ((our_fds > 0 || (nfds == 0 && tmop == &tmo)) && (retval == 0))
+    {
+      retval = -1;
+      errno = EINTR;
+    }
+
+  PGTK_TRACE("pgtk_select: leave.");
+  return retval;
+}
+
+
+/* Lisp window being scrolled.  Set when starting to interact with
+   a toolkit scroll bar, reset to nil when ending the interaction.  */
+
+static Lisp_Object window_being_scrolled;
+
+static void
+pgtk_send_scroll_bar_event (Lisp_Object window, enum scroll_bar_part part,
+                           int portion, int whole, bool horizontal)
+{
+  union buffered_input_event inev;
+
+  EVENT_INIT (inev.ie);
+
+  inev.ie.kind = horizontal ? HORIZONTAL_SCROLL_BAR_CLICK_EVENT : 
SCROLL_BAR_CLICK_EVENT;
+  inev.ie.frame_or_window = window;
+  inev.ie.arg = Qnil;
+  inev.ie.timestamp = 0;
+  inev.ie.code = 0;
+  inev.ie.part = part;
+  inev.ie.x = make_fixnum(portion);
+  inev.ie.y = make_fixnum(whole);
+  inev.ie.modifiers = 0;
+
+  evq_enqueue(&inev);
+}
+
+
+/* Scroll bar callback for GTK scroll bars.  WIDGET is the scroll
+   bar widget.  DATA is a pointer to the scroll_bar structure. */
+
+static gboolean
+xg_scroll_callback (GtkRange     *range,
+                    GtkScrollType scroll,
+                    gdouble       value,
+                    gpointer      user_data)
+{
+  int whole = 0, portion = 0;
+  struct scroll_bar *bar = user_data;
+  enum scroll_bar_part part = scroll_bar_nowhere;
+  GtkAdjustment *adj = GTK_ADJUSTMENT (gtk_range_get_adjustment (range));
+  PGTK_TRACE("xg_scroll_callback:");
+
+  if (xg_ignore_gtk_scrollbar) return false;
+  PGTK_TRACE("xg_scroll_callback: not ignored.");
+
+  PGTK_TRACE("xg_scroll_callback: scroll=%u.", scroll);
+  switch (scroll)
+    {
+    case GTK_SCROLL_JUMP:
+#if 0
+      /* Buttons 1 2 or 3 must be grabbed.  */
+      if (FRAME_DISPLAY_INFO (f)->grabbed != 0
+          && FRAME_DISPLAY_INFO (f)->grabbed < (1 << 4))
+#endif
+        {
+         if (bar->horizontal)
+           {
+             part = scroll_bar_horizontal_handle;
+             whole = (int)(gtk_adjustment_get_upper (adj) -
+                           gtk_adjustment_get_page_size (adj));
+             portion = min ((int)value, whole);
+             bar->dragging = portion;
+           }
+         else
+           {
+             part = scroll_bar_handle;
+             whole = gtk_adjustment_get_upper (adj) -
+               gtk_adjustment_get_page_size (adj);
+             portion = min ((int)value, whole);
+             bar->dragging = portion;
+           }
+       }
+      break;
+    case GTK_SCROLL_STEP_BACKWARD:
+      part = (bar->horizontal
+             ? scroll_bar_left_arrow : scroll_bar_up_arrow);
+      bar->dragging = -1;
+      break;
+    case GTK_SCROLL_STEP_FORWARD:
+      part = (bar->horizontal
+             ? scroll_bar_right_arrow : scroll_bar_down_arrow);
+      bar->dragging = -1;
+      break;
+    case GTK_SCROLL_PAGE_BACKWARD:
+      part = (bar->horizontal
+             ? scroll_bar_before_handle : scroll_bar_above_handle);
+      bar->dragging = -1;
+      break;
+    case GTK_SCROLL_PAGE_FORWARD:
+      part = (bar->horizontal
+             ? scroll_bar_after_handle : scroll_bar_below_handle);
+      bar->dragging = -1;
+      break;
+    default:
+      break;
+    }
+
+  PGTK_TRACE("xg_scroll_callback: part=%u, scroll_bar_nowhere=%d.", part, 
scroll_bar_nowhere);
+  if (part != scroll_bar_nowhere)
+    {
+      window_being_scrolled = bar->window;
+      pgtk_send_scroll_bar_event (bar->window, part, portion, whole,
+                                 bar->horizontal);
+    }
+
+  return false;
+}
+
+/* Callback for button release. Sets dragging to -1 when dragging is done.  */
+
+static gboolean
+xg_end_scroll_callback (GtkWidget *widget,
+                        GdkEventButton *event,
+                        gpointer user_data)
+{
+  struct scroll_bar *bar = user_data;
+  PGTK_TRACE("xg_end_scroll_callback:");
+  bar->dragging = -1;
+  if (WINDOWP (window_being_scrolled))
+    {
+      pgtk_send_scroll_bar_event (window_being_scrolled,
+                                 scroll_bar_end_scroll, 0, 0, bar->horizontal);
+      window_being_scrolled = Qnil;
+    }
+
+  return false;
+}
+
+#define SCROLL_BAR_NAME "verticalScrollBar"
+#define SCROLL_BAR_HORIZONTAL_NAME "horizontalScrollBar"
+
+/* Create the widget for scroll bar BAR on frame F.  Record the widget
+   and X window of the scroll bar in BAR.  */
+
+static void
+x_create_toolkit_scroll_bar (struct frame *f, struct scroll_bar *bar)
+{
+  const char *scroll_bar_name = SCROLL_BAR_NAME;
+
+  block_input ();
+  xg_create_scroll_bar (f, bar, G_CALLBACK (xg_scroll_callback),
+                        G_CALLBACK (xg_end_scroll_callback),
+                        scroll_bar_name);
+  unblock_input ();
+}
+
+static void
+x_create_horizontal_toolkit_scroll_bar (struct frame *f, struct scroll_bar 
*bar)
+{
+  const char *scroll_bar_name = SCROLL_BAR_HORIZONTAL_NAME;
+
+  block_input ();
+  xg_create_horizontal_scroll_bar (f, bar, G_CALLBACK (xg_scroll_callback),
+                                  G_CALLBACK (xg_end_scroll_callback),
+                                  scroll_bar_name);
+  unblock_input ();
+}
+
+/* Set the thumb size and position of scroll bar BAR.  We are currently
+   displaying PORTION out of a whole WHOLE, and our position POSITION.  */
+
+static void
+x_set_toolkit_scroll_bar_thumb (struct scroll_bar *bar, int portion, int 
position, int whole)
+{
+  xg_set_toolkit_scroll_bar_thumb (bar, portion, position, whole);
+}
+
+static void
+x_set_toolkit_horizontal_scroll_bar_thumb (struct scroll_bar *bar, int 
portion, int position, int whole)
+{
+  xg_set_toolkit_horizontal_scroll_bar_thumb (bar, portion, position, whole);
+}
+
+
+
+/* Create a scroll bar and return the scroll bar vector for it.  W is
+   the Emacs window on which to create the scroll bar. TOP, LEFT,
+   WIDTH and HEIGHT are the pixel coordinates and dimensions of the
+   scroll bar. */
+
+static struct scroll_bar *
+x_scroll_bar_create (struct window *w, int top, int left,
+                    int width, int height, bool horizontal)
+{
+  struct frame *f = XFRAME (w->frame);
+  struct scroll_bar *bar
+    = ALLOCATE_PSEUDOVECTOR (struct scroll_bar, prev, PVEC_OTHER);
+  Lisp_Object barobj;
+
+  block_input ();
+
+  if (horizontal)
+    x_create_horizontal_toolkit_scroll_bar (f, bar);
+  else
+    x_create_toolkit_scroll_bar (f, bar);
+
+  XSETWINDOW (bar->window, w);
+  bar->top = top;
+  bar->left = left;
+  bar->width = width;
+  bar->height = height;
+  bar->start = 0;
+  bar->end = 0;
+  bar->dragging = -1;
+  bar->horizontal = horizontal;
+
+  /* Add bar to its frame's list of scroll bars.  */
+  bar->next = FRAME_SCROLL_BARS (f);
+  bar->prev = Qnil;
+  XSETVECTOR (barobj, bar);
+  fset_scroll_bars (f, barobj);
+  if (!NILP (bar->next))
+    XSETVECTOR (XSCROLL_BAR (bar->next)->prev, bar);
+
+  /* Map the window/widget.  */
+  {
+    if (horizontal)
+      xg_update_horizontal_scrollbar_pos (f, bar->x_window, top,
+                                         left, width, max (height, 1));
+    else
+      xg_update_scrollbar_pos (f, bar->x_window, top,
+                              left, width, max (height, 1));
+    }
+
+  unblock_input ();
+  return bar;
+}
+
+/* Destroy scroll bar BAR, and set its Emacs window's scroll bar to
+   nil.  */
+
+static void
+x_scroll_bar_remove (struct scroll_bar *bar)
+{
+  struct frame *f = XFRAME (WINDOW_FRAME (XWINDOW (bar->window)));
+  block_input ();
+
+  xg_remove_scroll_bar (f, bar->x_window);
+
+  /* Dissociate this scroll bar from its window.  */
+  if (bar->horizontal)
+    wset_horizontal_scroll_bar (XWINDOW (bar->window), Qnil);
+  else
+    wset_vertical_scroll_bar (XWINDOW (bar->window), Qnil);
+
+  unblock_input ();
+}
+
+/* Set the handle of the vertical scroll bar for WINDOW to indicate
+   that we are displaying PORTION characters out of a total of WHOLE
+   characters, starting at POSITION.  If WINDOW has no scroll bar,
+   create one.  */
+
+static void
+pgtk_set_vertical_scroll_bar (struct window *w, int portion, int whole, int 
position)
+{
+  struct frame *f = XFRAME (w->frame);
+  Lisp_Object barobj;
+  struct scroll_bar *bar;
+  int top, height, left, width;
+  int window_y, window_height;
+
+  /* Get window dimensions.  */
+  window_box (w, ANY_AREA, 0, &window_y, 0, &window_height);
+  top = window_y;
+  height = window_height;
+  left = WINDOW_SCROLL_BAR_AREA_X (w);
+  width = WINDOW_SCROLL_BAR_AREA_WIDTH (w);
+  PGTK_TRACE("pgtk_set_vertical_scroll_bar: has_vertical_scroll_bar: %d", 
WINDOW_HAS_VERTICAL_SCROLL_BAR(w));
+  PGTK_TRACE("pgtk_set_vertical_scroll_bar: config_scroll_bar_width: %d", 
WINDOW_CONFIG_SCROLL_BAR_WIDTH(w));
+  PGTK_TRACE("pgtk_set_vertical_scroll_bar: scroll_bar_width: %d", 
w->scroll_bar_width);
+  PGTK_TRACE("pgtk_set_vertical_scroll_bar: config_scroll_bar_width: %d", 
FRAME_CONFIG_SCROLL_BAR_WIDTH (WINDOW_XFRAME (w)));
+  PGTK_TRACE("pgtk_set_vertical_scroll_bar: %dx%d+%d+%d", width, height, left, 
top);
+
+  /* Does the scroll bar exist yet?  */
+  if (NILP (w->vertical_scroll_bar))
+    {
+      if (width > 0 && height > 0)
+       {
+         block_input ();
+          pgtk_clear_area (f, left, top, width, height);
+         unblock_input ();
+       }
+
+      bar = x_scroll_bar_create (w, top, left, width, max (height, 1), false);
+    }
+  else
+    {
+      /* It may just need to be moved and resized.  */
+      unsigned int mask = 0;
+
+      bar = XSCROLL_BAR (w->vertical_scroll_bar);
+
+      block_input ();
+
+      if (left != bar->left)
+       mask |= 1;
+      if (top != bar->top)
+       mask |= 1;
+      if (width != bar->width)
+       mask |= 1;
+      if (height != bar->height)
+       mask |= 1;
+
+      /* Move/size the scroll bar widget.  */
+      if (mask)
+       {
+         /* Since toolkit scroll bars are smaller than the space reserved
+            for them on the frame, we have to clear "under" them.  */
+         if (width > 0 && height > 0)
+           pgtk_clear_area (f, left, top, width, height);
+          xg_update_scrollbar_pos (f, bar->x_window, top,
+                                  left, width, max (height, 1));
+       }
+
+      /* Remember new settings.  */
+      bar->left = left;
+      bar->top = top;
+      bar->width = width;
+      bar->height = height;
+
+      unblock_input ();
+    }
+
+  x_set_toolkit_scroll_bar_thumb (bar, portion, position, whole);
+
+  XSETVECTOR (barobj, bar);
+  wset_vertical_scroll_bar (w, barobj);
+}
+
+
+static void
+pgtk_set_horizontal_scroll_bar (struct window *w, int portion, int whole, int 
position)
+{
+  struct frame *f = XFRAME (w->frame);
+  Lisp_Object barobj;
+  struct scroll_bar *bar;
+  int top, height, left, width;
+  int window_x, window_width;
+  int pixel_width = WINDOW_PIXEL_WIDTH (w);
+
+  /* Get window dimensions.  */
+  window_box (w, ANY_AREA, &window_x, 0, &window_width, 0);
+  left = window_x;
+  width = window_width;
+  top = WINDOW_SCROLL_BAR_AREA_Y (w);
+  height = WINDOW_SCROLL_BAR_AREA_HEIGHT (w);
+
+  /* Does the scroll bar exist yet?  */
+  if (NILP (w->horizontal_scroll_bar))
+    {
+      if (width > 0 && height > 0)
+       {
+         block_input ();
+
+         /* Clear also part between window_width and
+            WINDOW_PIXEL_WIDTH.  */
+         pgtk_clear_area (f, left, top, pixel_width, height);
+         unblock_input ();
+       }
+
+      bar = x_scroll_bar_create (w, top, left, width, height, true);
+    }
+  else
+    {
+      /* It may just need to be moved and resized.  */
+      unsigned int mask = 0;
+
+      bar = XSCROLL_BAR (w->horizontal_scroll_bar);
+
+      block_input ();
+
+      if (left != bar->left)
+       mask |= 1;
+      if (top != bar->top)
+       mask |= 1;
+      if (width != bar->width)
+       mask |= 1;
+      if (height != bar->height)
+       mask |= 1;
+
+      /* Move/size the scroll bar widget.  */
+      if (mask)
+       {
+         /* Since toolkit scroll bars are smaller than the space reserved
+            for them on the frame, we have to clear "under" them.  */
+         if (width > 0 && height > 0)
+           pgtk_clear_area (f,
+                         WINDOW_LEFT_EDGE_X (w), top,
+                         pixel_width - WINDOW_RIGHT_DIVIDER_WIDTH (w), height);
+          xg_update_horizontal_scrollbar_pos (f, bar->x_window, top, left,
+                                             width, height);
+       }
+
+      /* Remember new settings.  */
+      bar->left = left;
+      bar->top = top;
+      bar->width = width;
+      bar->height = height;
+
+      unblock_input ();
+    }
+
+  x_set_toolkit_horizontal_scroll_bar_thumb (bar, portion, position, whole);
+
+  XSETVECTOR (barobj, bar);
+  wset_horizontal_scroll_bar (w, barobj);
+}
+
+/* The following three hooks are used when we're doing a thorough
+   redisplay of the frame.  We don't explicitly know which scroll bars
+   are going to be deleted, because keeping track of when windows go
+   away is a real pain - "Can you say set-window-configuration, boys
+   and girls?"  Instead, we just assert at the beginning of redisplay
+   that *all* scroll bars are to be removed, and then save a scroll bar
+   from the fiery pit when we actually redisplay its window.  */
+
+/* Arrange for all scroll bars on FRAME to be removed at the next call
+   to `*judge_scroll_bars_hook'.  A scroll bar may be spared if
+   `*redeem_scroll_bar_hook' is applied to its window before the judgment.  */
+
+static void
+pgtk_condemn_scroll_bars (struct frame *frame)
+{
+  if (!NILP (FRAME_SCROLL_BARS (frame)))
+    {
+      if (!NILP (FRAME_CONDEMNED_SCROLL_BARS (frame)))
+       {
+         /* Prepend scrollbars to already condemned ones.  */
+         Lisp_Object last = FRAME_SCROLL_BARS (frame);
+
+         while (!NILP (XSCROLL_BAR (last)->next))
+           last = XSCROLL_BAR (last)->next;
+
+         XSCROLL_BAR (last)->next = FRAME_CONDEMNED_SCROLL_BARS (frame);
+         XSCROLL_BAR (FRAME_CONDEMNED_SCROLL_BARS (frame))->prev = last;
+       }
+
+      fset_condemned_scroll_bars (frame, FRAME_SCROLL_BARS (frame));
+      fset_scroll_bars (frame, Qnil);
+    }
+}
+
+
+/* Un-mark WINDOW's scroll bar for deletion in this judgment cycle.
+   Note that WINDOW isn't necessarily condemned at all.  */
+
+static void
+pgtk_redeem_scroll_bar (struct window *w)
+{
+  struct scroll_bar *bar;
+  Lisp_Object barobj;
+  struct frame *f;
+
+  /* We can't redeem this window's scroll bar if it doesn't have one.  */
+  if (NILP (w->vertical_scroll_bar) && NILP (w->horizontal_scroll_bar))
+    emacs_abort ();
+
+  if (!NILP (w->vertical_scroll_bar) && WINDOW_HAS_VERTICAL_SCROLL_BAR (w))
+    {
+      bar = XSCROLL_BAR (w->vertical_scroll_bar);
+      /* Unlink it from the condemned list.  */
+      f = XFRAME (WINDOW_FRAME (w));
+      if (NILP (bar->prev))
+       {
+         /* If the prev pointer is nil, it must be the first in one of
+            the lists.  */
+         if (EQ (FRAME_SCROLL_BARS (f), w->vertical_scroll_bar))
+           /* It's not condemned.  Everything's fine.  */
+           goto horizontal;
+         else if (EQ (FRAME_CONDEMNED_SCROLL_BARS (f),
+                      w->vertical_scroll_bar))
+           fset_condemned_scroll_bars (f, bar->next);
+         else
+           /* If its prev pointer is nil, it must be at the front of
+              one or the other!  */
+           emacs_abort ();
+       }
+      else
+       XSCROLL_BAR (bar->prev)->next = bar->next;
+
+      if (! NILP (bar->next))
+       XSCROLL_BAR (bar->next)->prev = bar->prev;
+
+      bar->next = FRAME_SCROLL_BARS (f);
+      bar->prev = Qnil;
+      XSETVECTOR (barobj, bar);
+      fset_scroll_bars (f, barobj);
+      if (! NILP (bar->next))
+       XSETVECTOR (XSCROLL_BAR (bar->next)->prev, bar);
+    }
+
+ horizontal:
+  if (!NILP (w->horizontal_scroll_bar) && WINDOW_HAS_HORIZONTAL_SCROLL_BAR (w))
+    {
+      bar = XSCROLL_BAR (w->horizontal_scroll_bar);
+      /* Unlink it from the condemned list.  */
+      f = XFRAME (WINDOW_FRAME (w));
+      if (NILP (bar->prev))
+       {
+         /* If the prev pointer is nil, it must be the first in one of
+            the lists.  */
+         if (EQ (FRAME_SCROLL_BARS (f), w->horizontal_scroll_bar))
+           /* It's not condemned.  Everything's fine.  */
+           return;
+         else if (EQ (FRAME_CONDEMNED_SCROLL_BARS (f),
+                      w->horizontal_scroll_bar))
+           fset_condemned_scroll_bars (f, bar->next);
+         else
+           /* If its prev pointer is nil, it must be at the front of
+              one or the other!  */
+           emacs_abort ();
+       }
+      else
+       XSCROLL_BAR (bar->prev)->next = bar->next;
+
+      if (! NILP (bar->next))
+       XSCROLL_BAR (bar->next)->prev = bar->prev;
+
+      bar->next = FRAME_SCROLL_BARS (f);
+      bar->prev = Qnil;
+      XSETVECTOR (barobj, bar);
+      fset_scroll_bars (f, barobj);
+      if (! NILP (bar->next))
+       XSETVECTOR (XSCROLL_BAR (bar->next)->prev, bar);
+    }
+}
+
+/* Remove all scroll bars on FRAME that haven't been saved since the
+   last call to `*condemn_scroll_bars_hook'.  */
+
+static void
+pgtk_judge_scroll_bars (struct frame *f)
+{
+  Lisp_Object bar, next;
+
+  bar = FRAME_CONDEMNED_SCROLL_BARS (f);
+
+  /* Clear out the condemned list now so we won't try to process any
+     more events on the hapless scroll bars.  */
+  fset_condemned_scroll_bars (f, Qnil);
+
+  for (; ! NILP (bar); bar = next)
+    {
+      struct scroll_bar *b = XSCROLL_BAR (bar);
+
+      x_scroll_bar_remove (b);
+
+      next = b->next;
+      b->next = b->prev = Qnil;
+    }
+
+  /* Now there should be no references to the condemned scroll bars,
+     and they should get garbage-collected.  */
+}
+
+static void set_fullscreen_state(struct frame *f)
+{
+  GtkWindow *widget = GTK_WINDOW(FRAME_GTK_OUTER_WIDGET(f));
+  switch (f->want_fullscreen) {
+  case FULLSCREEN_NONE:
+    PGTK_TRACE("pgtk_fullscreen_hook: none.");
+    gtk_window_unfullscreen(widget);
+    gtk_window_unmaximize(widget);
+    store_frame_param(f, Qfullscreen, Qnil);
+    break;
+
+  case FULLSCREEN_BOTH:
+    PGTK_TRACE("pgtk_fullscreen_hook: both.");
+    gtk_window_unmaximize(widget);
+    gtk_window_fullscreen(widget);
+    store_frame_param(f, Qfullscreen, Qfullboth);
+    break;
+
+  case FULLSCREEN_MAXIMIZED:
+    PGTK_TRACE("pgtk_fullscreen_hook: maximized.");
+    gtk_window_unfullscreen(widget);
+    gtk_window_maximize(widget);
+    store_frame_param(f, Qfullscreen, Qmaximized);
+    break;
+
+  case FULLSCREEN_WIDTH:
+  case FULLSCREEN_HEIGHT:
+    PGTK_TRACE("pgtk_fullscreen_hook: width or height.");
+    /* Not supported by gtk. Ignore them.*/
+  }
+
+  f->want_fullscreen = FULLSCREEN_NONE;
+}
+
+static void
+pgtk_fullscreen_hook (struct frame *f)
+{
+  PGTK_TRACE("pgtk_fullscreen_hook:");
+  if (FRAME_VISIBLE_P (f))
+    {
+      block_input ();
+      set_fullscreen_state(f);
+      unblock_input ();
+    }
+}
+
+static Lisp_Object
+pgtk_menu_show (struct frame *f, int x, int y, int menuflags,
+            Lisp_Object title, const char **error_name)
+{
+  // not implemented.
+  return Qnil;
+}
+
+/* This function is called when the last frame on a display is deleted. */
+void
+pgtk_delete_terminal (struct terminal *terminal)
+{
+  struct pgtk_display_info *dpyinfo = terminal->display_info.pgtk;
+
+  /* Protect against recursive calls.  delete_frame in
+     delete_terminal calls us back when it deletes our last frame.  */
+  if (!terminal->name)
+    return;
+
+  block_input ();
+
+  /* Normally, the display is available...  */
+  if (dpyinfo->gdpy)
+    {
+      x_destroy_all_bitmaps (dpyinfo);
+
+      xg_display_close (dpyinfo->gdpy);
+
+      /* Do not close the connection here because it's already closed
+        by X(t)CloseDisplay (Bug#18403).  */
+      dpyinfo->gdpy = NULL;
+    }
+
+  pgtk_delete_display (dpyinfo);
+  unblock_input ();
+}
+
+/* Store F's background color into *BGCOLOR.  */
+static void
+pgtk_query_frame_background_color (struct frame *f, Emacs_Color *bgcolor)
+{
+  bgcolor->pixel = FRAME_BACKGROUND_PIXEL (f);
+  pgtk_query_color (f, bgcolor);
+}
+
+static void
+pgtk_free_pixmap (struct frame *_f, Emacs_Pixmap pixmap)
+{
+  if (pixmap)
+    {
+      xfree (pixmap->data);
+      xfree (pixmap);
+    }
+}
+
+static struct terminal *
+pgtk_create_terminal (struct pgtk_display_info *dpyinfo)
+/* --------------------------------------------------------------------------
+      Set up use of Gtk before we make the first connection.
+   -------------------------------------------------------------------------- 
*/
+{
+  struct terminal *terminal;
+
+  terminal = create_terminal (output_pgtk, &pgtk_redisplay_interface);
+
+  terminal->display_info.pgtk = dpyinfo;
+  dpyinfo->terminal = terminal;
+
+  terminal->clear_frame_hook = pgtk_clear_frame;
+  terminal->ring_bell_hook = pgtk_ring_bell;
+  terminal->update_begin_hook = pgtk_update_begin;
+  terminal->update_end_hook = pgtk_update_end;
+  terminal->read_socket_hook = pgtk_read_socket;
+  // terminal->frame_up_to_date_hook = pgtk_frame_up_to_date;
+  terminal->mouse_position_hook = pgtk_mouse_position;
+  // terminal->frame_rehighlight_hook = pgtk_frame_rehighlight;
+  // terminal->frame_raise_lower_hook = pgtk_frame_raise_lower;
+  terminal->fullscreen_hook = pgtk_fullscreen_hook;
+  terminal->menu_show_hook = pgtk_menu_show;
+  // terminal->popup_dialog_hook = pgtk_popup_dialog;
+  terminal->set_vertical_scroll_bar_hook = pgtk_set_vertical_scroll_bar;
+  terminal->set_horizontal_scroll_bar_hook = pgtk_set_horizontal_scroll_bar;
+  terminal->condemn_scroll_bars_hook = pgtk_condemn_scroll_bars;
+  terminal->redeem_scroll_bar_hook = pgtk_redeem_scroll_bar;
+  terminal->judge_scroll_bars_hook = pgtk_judge_scroll_bars;
+  terminal->delete_frame_hook = x_destroy_window;
+  terminal->delete_terminal_hook = pgtk_delete_terminal;
+  terminal->set_window_size_hook = pgtk_set_window_size;
+  terminal->query_colors = pgtk_query_colors;
+  terminal->get_focus_frame = x_get_focus_frame;
+  terminal->focus_frame_hook = pgtk_focus_frame;
+  terminal->set_frame_offset_hook = x_set_offset;
+  terminal->free_pixmap = pgtk_free_pixmap;
+
+  /* Other hooks are NULL by default.  */
+
+  return terminal;
+}
+
+struct pgtk_window_is_of_frame_recursive_t {
+  GdkWindow *window;
+  bool result;
+};
+
+static void
+pgtk_window_is_of_frame_recursive(GtkWidget *widget, gpointer data)
+{
+  struct pgtk_window_is_of_frame_recursive_t *datap = data;
+
+  if (datap->result)
+    return;
+
+  if (gtk_widget_get_window(widget) == datap->window) {
+    datap->result = true;
+    return;
+  }
+
+  if (GTK_IS_CONTAINER(widget))
+    gtk_container_foreach(GTK_CONTAINER(widget), 
pgtk_window_is_of_frame_recursive, datap);
+}
+
+static bool
+pgtk_window_is_of_frame(struct frame *f, GdkWindow *window)
+{
+  struct pgtk_window_is_of_frame_recursive_t data;
+  data.window = window;
+  data.result = false;
+  pgtk_window_is_of_frame_recursive(FRAME_GTK_OUTER_WIDGET(f), &data);
+  return data.result;
+}
+
+/* Like x_window_to_frame but also compares the window with the widget's
+   windows.  */
+static struct frame *
+pgtk_any_window_to_frame (GdkWindow *window)
+{
+  Lisp_Object tail, frame;
+  struct frame *f, *found = NULL;
+
+  if (window == NULL)
+    return NULL;
+
+  FOR_EACH_FRAME (tail, frame)
+    {
+      if (found)
+        break;
+      f = XFRAME (frame);
+      if (FRAME_PGTK_P (f))
+       {
+         if (pgtk_window_is_of_frame(f, window))
+           found = f;
+       }
+    }
+
+  return found;
+}
+
+static gboolean
+pgtk_handle_event(GtkWidget *widget, GdkEvent *event, gpointer *data)
+{
+#ifdef PGTK_DEBUG
+  const char *type_name = G_OBJECT_TYPE_NAME(widget);
+  switch (event->type) {
+  case GDK_NOTHING:               PGTK_TRACE("GDK_NOTHING"); break;
+  case GDK_DELETE:                PGTK_TRACE("GDK_DELETE"); break;
+  case GDK_DESTROY:               PGTK_TRACE("GDK_DESTROY"); break;
+  case GDK_EXPOSE:                PGTK_TRACE("GDK_EXPOSE"); break;
+  case GDK_MOTION_NOTIFY:         PGTK_TRACE("GDK_MOTION_NOTIFY"); break;
+  case GDK_BUTTON_PRESS:          PGTK_TRACE("GDK_BUTTON_PRESS"); break;
+  case GDK_2BUTTON_PRESS:         PGTK_TRACE("GDK_2BUTTON_PRESS"); break;
+  case GDK_3BUTTON_PRESS:         PGTK_TRACE("GDK_3BUTTON_PRESS"); break;
+  case GDK_BUTTON_RELEASE:        PGTK_TRACE("GDK_BUTTON_RELEASE"); break;
+  case GDK_KEY_PRESS:             PGTK_TRACE("GDK_KEY_PRESS"); break;
+  case GDK_KEY_RELEASE:           PGTK_TRACE("GDK_KEY_RELEASE"); break;
+  case GDK_ENTER_NOTIFY:          PGTK_TRACE("GDK_ENTER_NOTIFY"); break;
+  case GDK_LEAVE_NOTIFY:          PGTK_TRACE("GDK_LEAVE_NOTIFY"); break;
+  case GDK_FOCUS_CHANGE:          PGTK_TRACE("GDK_FOCUS_CHANGE"); break;
+  case GDK_CONFIGURE:             PGTK_TRACE("GDK_CONFIGURE"); break;
+  case GDK_MAP:                   PGTK_TRACE("GDK_MAP"); break;
+  case GDK_UNMAP:                 PGTK_TRACE("GDK_UNMAP"); break;
+  case GDK_PROPERTY_NOTIFY:       PGTK_TRACE("GDK_PROPERTY_NOTIFY"); break;
+  case GDK_SELECTION_CLEAR:       PGTK_TRACE("GDK_SELECTION_CLEAR"); break;
+  case GDK_SELECTION_REQUEST:     PGTK_TRACE("GDK_SELECTION_REQUEST"); break;
+  case GDK_SELECTION_NOTIFY:      PGTK_TRACE("GDK_SELECTION_NOTIFY"); break;
+  case GDK_PROXIMITY_IN:          PGTK_TRACE("GDK_PROXIMITY_IN"); break;
+  case GDK_PROXIMITY_OUT:         PGTK_TRACE("GDK_PROXIMITY_OUT"); break;
+  case GDK_DRAG_ENTER:            PGTK_TRACE("GDK_DRAG_ENTER"); break;
+  case GDK_DRAG_LEAVE:            PGTK_TRACE("GDK_DRAG_LEAVE"); break;
+  case GDK_DRAG_MOTION:           PGTK_TRACE("GDK_DRAG_MOTION"); break;
+  case GDK_DRAG_STATUS:           PGTK_TRACE("GDK_DRAG_STATUS"); break;
+  case GDK_DROP_START:            PGTK_TRACE("GDK_DROP_START"); break;
+  case GDK_DROP_FINISHED:         PGTK_TRACE("GDK_DROP_FINISHED"); break;
+  case GDK_CLIENT_EVENT:          PGTK_TRACE("GDK_CLIENT_EVENT"); break;
+  case GDK_VISIBILITY_NOTIFY:     PGTK_TRACE("GDK_VISIBILITY_NOTIFY"); break;
+  case GDK_SCROLL:                PGTK_TRACE("GDK_SCROLL"); break;
+  case GDK_WINDOW_STATE:          PGTK_TRACE("GDK_WINDOW_STATE"); break;
+  case GDK_SETTING:               PGTK_TRACE("GDK_SETTING"); break;
+  case GDK_OWNER_CHANGE:          PGTK_TRACE("GDK_OWNER_CHANGE"); break;
+  case GDK_GRAB_BROKEN:           PGTK_TRACE("GDK_GRAB_BROKEN"); break;
+  case GDK_DAMAGE:                PGTK_TRACE("GDK_DAMAGE"); break;
+  case GDK_TOUCH_BEGIN:           PGTK_TRACE("GDK_TOUCH_BEGIN"); break;
+  case GDK_TOUCH_UPDATE:          PGTK_TRACE("GDK_TOUCH_UPDATE"); break;
+  case GDK_TOUCH_END:             PGTK_TRACE("GDK_TOUCH_END"); break;
+  case GDK_TOUCH_CANCEL:          PGTK_TRACE("GDK_TOUCH_CANCEL"); break;
+  case GDK_TOUCHPAD_SWIPE:        PGTK_TRACE("GDK_TOUCHPAD_SWIPE"); break;
+  case GDK_TOUCHPAD_PINCH:        PGTK_TRACE("GDK_TOUCHPAD_PINCH"); break;
+  case GDK_PAD_BUTTON_PRESS:      PGTK_TRACE("GDK_PAD_BUTTON_PRESS"); break;
+  case GDK_PAD_BUTTON_RELEASE:    PGTK_TRACE("GDK_PAD_BUTTON_RELEASE"); break;
+  case GDK_PAD_RING:              PGTK_TRACE("GDK_PAD_RING"); break;
+  case GDK_PAD_STRIP:             PGTK_TRACE("GDK_PAD_STRIP"); break;
+  case GDK_PAD_GROUP_MODE:        PGTK_TRACE("GDK_PAD_GROUP_MODE"); break;
+  default:                        PGTK_TRACE("GDK_EVENT %d", event->type);
+  }
+  PGTK_TRACE(" Widget is %s", type_name);
+#endif
+  return FALSE;
+}
+
+static void
+pgtk_fill_rectangle(struct frame *f, unsigned long color, int x, int y, int 
width, int height)
+{
+  PGTK_TRACE("pgtk_fill_rectangle");
+  cairo_t *cr;
+  cr = pgtk_begin_cr_clip (f);
+  pgtk_set_cr_source_with_color (f, color);
+  cairo_rectangle (cr, x, y, width, height);
+  cairo_fill (cr);
+  pgtk_end_cr_clip (f);
+}
+
+void
+pgtk_clear_under_internal_border (struct frame *f)
+{
+  PGTK_TRACE("pgtk_clear_under_internal_border");
+  if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
+    {
+      int border = FRAME_INTERNAL_BORDER_WIDTH (f);
+      int width = FRAME_PIXEL_WIDTH (f);
+      int height = FRAME_PIXEL_HEIGHT (f);
+      int margin = 0;
+      struct face *face = FACE_FROM_ID_OR_NULL (f, INTERNAL_BORDER_FACE_ID);
+
+      block_input ();
+
+      if (face)
+       {
+         pgtk_fill_rectangle (f, color, 0, margin, width, border);
+         pgtk_fill_rectangle (f, color, 0, 0, border, height);
+         pgtk_fill_rectangle (f, color, width - border, 0, border, height);
+         pgtk_fill_rectangle (f, color, 0, height - border, width, border);
+       }
+      else
+       {
+         pgtk_clear_area (f, 0, 0, border, height);
+         pgtk_clear_area (f, 0, margin, width, border);
+         pgtk_clear_area (f, width - border, 0, border, height);
+         pgtk_clear_area (f, 0, height - border, width, border);
+       }
+
+      unblock_input ();
+    }
+}
+
+static void print_widget_tree_recursive(GtkWidget *w, gpointer user_data)
+{
+  const char *indent = user_data;
+  char buf[1024] = "";
+  int len = 0;
+  len += sprintf(buf + len, "%s", indent);
+  len += sprintf(buf + len, "%p %s mapped:%d visible:%d", w, 
G_OBJECT_TYPE_NAME(w), gtk_widget_get_mapped(w), gtk_widget_get_visible(w));
+  gint wd, hi;
+  gtk_widget_get_size_request(w, &wd, &hi);
+  len += sprintf(buf + len, " size_req:%dx%d", wd, hi);
+  GtkAllocation alloc;
+  gtk_widget_get_allocation(w, &alloc);
+  len += sprintf(buf + len, " alloc:%dx%d+%d+%d", alloc.width, alloc.height, 
alloc.x, alloc.y);
+  len += sprintf(buf + len, " haswin:%d", gtk_widget_get_has_window(w));
+  len += sprintf(buf + len, " gdkwin:%p", gtk_widget_get_window(w));
+  PGTK_TRACE("%s", buf);
+
+  if (GTK_IS_CONTAINER(w)) {
+    strcpy(buf, indent);
+    strcat(buf, "  ");
+    gtk_container_foreach(GTK_CONTAINER(w), print_widget_tree_recursive, buf);
+  }
+}
+
+static void print_widget_tree(GtkWidget *w)
+{
+  char indent[1] = "";
+  w = gtk_widget_get_toplevel(w);
+  print_widget_tree_recursive(w, indent);
+}
+
+static gboolean
+pgtk_handle_draw(GtkWidget *widget, cairo_t *cr, gpointer *data)
+{
+  struct frame *f;
+
+  PGTK_TRACE("pgtk_handle_draw");
+
+  print_widget_tree(widget);
+
+  GdkWindow *win = gtk_widget_get_window(widget);
+
+  PGTK_TRACE("  win=%p", win);
+  if (win != NULL) {
+    cairo_surface_t *src = NULL;
+    f = pgtk_any_window_to_frame(win);
+    PGTK_TRACE("  f=%p", f);
+    if (f != NULL) {
+      src = FRAME_X_OUTPUT(f)->cr_surface_visible_bell;
+      if (src == NULL)
+       src = FRAME_CR_SURFACE(f);
+    }
+    PGTK_TRACE("  surface=%p", src);
+    if (src != NULL) {
+      PGTK_TRACE("  resized_p=%d", f->resized_p);
+      PGTK_TRACE("  garbaged=%d", f->garbaged);
+      PGTK_TRACE("  scroll_bar_width=%f", (double) PGTK_SCROLL_BAR_WIDTH(f));
+      // PGTK_TRACE("  scroll_bar_adjust=%d", PGTK_SCROLL_BAR_ADJUST(f));
+      PGTK_TRACE("  scroll_bar_cols=%d", FRAME_SCROLL_BAR_COLS(f));
+      PGTK_TRACE("  column_width=%d", FRAME_COLUMN_WIDTH(f));
+      cairo_set_source_surface(cr, src, 0, 0);
+      cairo_paint(cr);
+    }
+  }
+  return FALSE;
+}
+
+static void size_allocate(GtkWidget *widget, GtkAllocation *alloc, gpointer 
*user_data)
+{
+  PGTK_TRACE("size-alloc: %dx%d+%d+%d.", alloc->width, alloc->height, 
alloc->x, alloc->y);
+
+  struct frame *f = pgtk_any_window_to_frame (gtk_widget_get_window(widget));
+  if (f) {
+    PGTK_TRACE("%dx%d", alloc->width, alloc->height);
+    xg_frame_resized(f, alloc->width, alloc->height);
+  }
+}
+
+int
+pgtk_gtk_to_emacs_modifiers (int state)
+{
+  int mod_ctrl = ctrl_modifier;
+  int mod_meta = meta_modifier;
+  int mod_alt  = alt_modifier;
+  int mod_hyper = hyper_modifier;
+  int mod_super = super_modifier;
+  Lisp_Object tem;
+
+  tem = Fget (Vx_ctrl_keysym, Qmodifier_value);
+  if (INTEGERP (tem)) mod_ctrl = XFIXNUM (tem) & INT_MAX;
+  tem = Fget (Vx_alt_keysym, Qmodifier_value);
+  if (INTEGERP (tem)) mod_alt = XFIXNUM (tem) & INT_MAX;
+  tem = Fget (Vx_meta_keysym, Qmodifier_value);
+  if (INTEGERP (tem)) mod_meta = XFIXNUM (tem) & INT_MAX;
+  tem = Fget (Vx_hyper_keysym, Qmodifier_value);
+  if (INTEGERP (tem)) mod_hyper = XFIXNUM (tem) & INT_MAX;
+  tem = Fget (Vx_super_keysym, Qmodifier_value);
+  if (INTEGERP (tem)) mod_super = XFIXNUM (tem) & INT_MAX;
+
+  return (  ((state & GDK_SHIFT_MASK)     ? shift_modifier : 0)
+            | ((state & GDK_CONTROL_MASK) ? mod_ctrl   : 0)
+            | ((state & GDK_META_MASK)   ? mod_meta    : 0)
+            | ((state & GDK_MOD1_MASK)   ? mod_alt     : 0)
+            | ((state & GDK_SUPER_MASK)          ? mod_super   : 0)
+            | ((state & GDK_HYPER_MASK)          ? mod_hyper   : 0));
+}
+
+static int
+pgtk_emacs_to_gtk_modifiers (EMACS_INT state)
+{
+  EMACS_INT mod_ctrl = ctrl_modifier;
+  EMACS_INT mod_meta = meta_modifier;
+  EMACS_INT mod_alt  = alt_modifier;
+  EMACS_INT mod_hyper = hyper_modifier;
+  EMACS_INT mod_super = super_modifier;
+
+  Lisp_Object tem;
+
+  tem = Fget (Vx_ctrl_keysym, Qmodifier_value);
+  if (INTEGERP (tem)) mod_ctrl = XFIXNUM (tem);
+  tem = Fget (Vx_alt_keysym, Qmodifier_value);
+  if (INTEGERP (tem)) mod_alt = XFIXNUM (tem);
+  tem = Fget (Vx_meta_keysym, Qmodifier_value);
+  if (INTEGERP (tem)) mod_meta = XFIXNUM (tem);
+  tem = Fget (Vx_hyper_keysym, Qmodifier_value);
+  if (INTEGERP (tem)) mod_hyper = XFIXNUM (tem);
+  tem = Fget (Vx_super_keysym, Qmodifier_value);
+  if (INTEGERP (tem)) mod_super = XFIXNUM (tem);
+
+
+  return (  ((state & mod_alt)         ? GDK_MOD1_MASK    : 0)
+            | ((state & mod_super)     ? GDK_SUPER_MASK   : 0)
+            | ((state & mod_hyper)     ? GDK_HYPER_MASK   : 0)
+            | ((state & shift_modifier)        ? GDK_SHIFT_MASK   : 0)
+            | ((state & mod_ctrl)      ? GDK_CONTROL_MASK : 0)
+            | ((state & mod_meta)      ? GDK_META_MASK    : 0));
+}
+
+#define IsCursorKey(keysym)       (0xff50 <= (keysym) && (keysym) < 0xff60)
+#define IsMiscFunctionKey(keysym) (0xff60 <= (keysym) && (keysym) < 0xff6c)
+#define IsKeypadKey(keysym)       (0xff80 <= (keysym) && (keysym) < 0xffbe)
+#define IsFunctionKey(keysym)     (0xffbe <= (keysym) && (keysym) < 0xffe1)
+
+static gboolean key_press_event(GtkWidget *widget, GdkEvent *event, gpointer 
*user_data)
+{
+  struct coding_system coding;
+  union buffered_input_event inev;
+  ptrdiff_t nbytes = 0;
+  Mouse_HLInfo *hlinfo;
+
+  USE_SAFE_ALLOCA;
+
+  PGTK_TRACE("key_press_event");
+
+  EVENT_INIT (inev.ie);
+  inev.ie.kind = NO_EVENT;
+  inev.ie.arg = Qnil;
+
+  struct frame *f = pgtk_any_window_to_frame(gtk_widget_get_window(widget));
+  hlinfo = MOUSE_HL_INFO(f);
+
+  /* If mouse-highlight is an integer, input clears out
+     mouse highlighting.  */
+  if (!hlinfo->mouse_face_hidden && INTEGERP (Vmouse_highlight))
+    {
+      clear_mouse_face (hlinfo);
+      hlinfo->mouse_face_hidden = true;
+    }
+
+  if (f != 0)
+    {
+      guint keysym, orig_keysym;
+      /* al%imercury@uunet.uu.net says that making this 81
+        instead of 80 fixed a bug whereby meta chars made
+        his Emacs hang.
+
+        It seems that some version of XmbLookupString has
+        a bug of not returning XBufferOverflow in
+        status_return even if the input is too long to
+        fit in 81 bytes.  So, we must prepare sufficient
+        bytes for copy_buffer.  513 bytes (256 chars for
+        two-byte character set) seems to be a fairly good
+        approximation.  -- 2000.8.10 handa@etl.go.jp  */
+      unsigned char copy_buffer[513];
+      unsigned char *copy_bufptr = copy_buffer;
+      int copy_bufsiz = sizeof (copy_buffer);
+      int modifiers;
+      Lisp_Object coding_system = Qlatin_1;
+      Lisp_Object c;
+      guint state = event->key.state;
+
+      state |= pgtk_emacs_to_gtk_modifiers (extra_keyboard_modifiers);
+      modifiers = state;
+
+      /* This will have to go some day...  */
+
+      /* make_lispy_event turns chars into control chars.
+        Don't do it here because XLookupString is too eager.  */
+      state &= ~GDK_CONTROL_MASK;
+      state &= ~(GDK_META_MASK
+                | GDK_SUPER_MASK
+                | GDK_HYPER_MASK
+                | GDK_MOD1_MASK);
+
+      nbytes = event->key.length;
+      if (nbytes > copy_bufsiz)
+       nbytes = copy_bufsiz;
+      memcpy(copy_bufptr, event->key.string, nbytes);
+
+      keysym = event->key.keyval;
+      orig_keysym = keysym;
+
+      /* Common for all keysym input events.  */
+      XSETFRAME (inev.ie.frame_or_window, f);
+      inev.ie.modifiers = pgtk_gtk_to_emacs_modifiers (modifiers);
+      inev.ie.timestamp = event->key.time;
+
+      /* First deal with keysyms which have defined
+        translations to characters.  */
+      if (keysym >= 32 && keysym < 128)
+       /* Avoid explicitly decoding each ASCII character.  */
+       {
+         inev.ie.kind = ASCII_KEYSTROKE_EVENT;
+         inev.ie.code = keysym;
+         goto done;
+       }
+
+      /* Keysyms directly mapped to Unicode characters.  */
+      if (keysym >= 0x01000000 && keysym <= 0x0110FFFF)
+       {
+         if (keysym < 0x01000080)
+           inev.ie.kind = ASCII_KEYSTROKE_EVENT;
+         else
+           inev.ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT;
+         inev.ie.code = keysym & 0xFFFFFF;
+         goto done;
+       }
+
+      /* Now non-ASCII.  */
+      if (HASH_TABLE_P (Vpgtk_keysym_table)
+         && (c = Fgethash (make_fixnum (keysym),
+                           Vpgtk_keysym_table,
+                           Qnil),
+             FIXNATP (c)))
+       {
+         inev.ie.kind = (SINGLE_BYTE_CHAR_P (XFIXNAT (c))
+                         ? ASCII_KEYSTROKE_EVENT
+                         : MULTIBYTE_CHAR_KEYSTROKE_EVENT);
+         inev.ie.code = XFIXNAT (c);
+         goto done;
+       }
+
+      /* Random non-modifier sorts of keysyms.  */
+      if (((keysym >= GDK_KEY_BackSpace && keysym <= GDK_KEY_Escape)
+          || keysym == GDK_KEY_Delete
+#ifdef GDK_KEY_ISO_Left_Tab
+          || (keysym >= GDK_KEY_ISO_Left_Tab
+              && keysym <= GDK_KEY_ISO_Enter)
+#endif
+          || IsCursorKey (keysym) /* 0xff50 <= x < 0xff60 */
+          || IsMiscFunctionKey (keysym) /* 0xff60 <= x < VARIES */
+#ifdef HPUX
+          /* This recognizes the "extended function
+             keys".  It seems there's no cleaner way.
+             Test IsModifierKey to avoid handling
+             mode_switch incorrectly.  */
+          || (GDK_KEY_Select <= keysym && keysym < GDK_KEY_KP_Space)
+#endif
+#ifdef GDK_KEY_dead_circumflex
+          || orig_keysym == GDK_KEY_dead_circumflex
+#endif
+#ifdef GDK_KEY_dead_grave
+          || orig_keysym == GDK_KEY_dead_grave
+#endif
+#ifdef GDK_KEY_dead_tilde
+          || orig_keysym == GDK_KEY_dead_tilde
+#endif
+#ifdef GDK_KEY_dead_diaeresis
+          || orig_keysym == GDK_KEY_dead_diaeresis
+#endif
+#ifdef GDK_KEY_dead_macron
+          || orig_keysym == GDK_KEY_dead_macron
+#endif
+#ifdef GDK_KEY_dead_degree
+          || orig_keysym == GDK_KEY_dead_degree
+#endif
+#ifdef GDK_KEY_dead_acute
+          || orig_keysym == GDK_KEY_dead_acute
+#endif
+#ifdef GDK_KEY_dead_cedilla
+          || orig_keysym == GDK_KEY_dead_cedilla
+#endif
+#ifdef GDK_KEY_dead_breve
+          || orig_keysym == GDK_KEY_dead_breve
+#endif
+#ifdef GDK_KEY_dead_ogonek
+          || orig_keysym == GDK_KEY_dead_ogonek
+#endif
+#ifdef GDK_KEY_dead_caron
+          || orig_keysym == GDK_KEY_dead_caron
+#endif
+#ifdef GDK_KEY_dead_doubleacute
+          || orig_keysym == GDK_KEY_dead_doubleacute
+#endif
+#ifdef GDK_KEY_dead_abovedot
+          || orig_keysym == GDK_KEY_dead_abovedot
+#endif
+          || IsKeypadKey (keysym) /* 0xff80 <= x < 0xffbe */
+          || IsFunctionKey (keysym) /* 0xffbe <= x < 0xffe1 */
+          /* Any "vendor-specific" key is ok.  */
+          || (orig_keysym & (1 << 28))
+          || (keysym != GDK_KEY_VoidSymbol && nbytes == 0))
+         && ! (event->key.is_modifier
+               /* The symbols from GDK_KEY_ISO_Lock
+                  to GDK_KEY_ISO_Last_Group_Lock
+                  don't have real modifiers but
+                  should be treated similarly to
+                  Mode_switch by Emacs. */
+#if defined GDK_KEY_ISO_Lock && defined GDK_KEY_ISO_Last_Group_Lock
+               || (GDK_KEY_ISO_Lock <= orig_keysym
+                   && orig_keysym <= GDK_KEY_ISO_Last_Group_Lock)
+#endif
+               ))
+       {
+         STORE_KEYSYM_FOR_DEBUG (keysym);
+         /* make_lispy_event will convert this to a symbolic
+            key.  */
+         inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT;
+         inev.ie.code = keysym;
+         goto done;
+       }
+
+      {        /* Raw bytes, not keysym.  */
+       ptrdiff_t i;
+       int nchars, len;
+
+       for (i = 0, nchars = 0; i < nbytes; i++)
+         {
+           if (ASCII_CHAR_P (copy_bufptr[i]))
+             nchars++;
+           STORE_KEYSYM_FOR_DEBUG (copy_bufptr[i]);
+         }
+
+       if (nchars < nbytes)
+         {
+           /* Decode the input data.  */
+
+           /* The input should be decoded with `coding_system'
+              which depends on which X*LookupString function
+              we used just above and the locale.  */
+           setup_coding_system (coding_system, &coding);
+           coding.src_multibyte = false;
+           coding.dst_multibyte = true;
+           /* The input is converted to events, thus we can't
+              handle composition.  Anyway, there's no XIM that
+              gives us composition information.  */
+           coding.common_flags &= ~CODING_ANNOTATION_MASK;
+
+           SAFE_NALLOCA (coding.destination, MAX_MULTIBYTE_LENGTH,
+                         nbytes);
+           coding.dst_bytes = MAX_MULTIBYTE_LENGTH * nbytes;
+           coding.mode |= CODING_MODE_LAST_BLOCK;
+           decode_coding_c_string (&coding, copy_bufptr, nbytes, Qnil);
+           nbytes = coding.produced;
+           nchars = coding.produced_char;
+           copy_bufptr = coding.destination;
+         }
+
+       /* Convert the input data to a sequence of
+          character events.  */
+       for (i = 0; i < nbytes; i += len)
+         {
+           int ch;
+           if (nchars == nbytes)
+             ch = copy_bufptr[i], len = 1;
+           else
+             ch = string_char_and_length (copy_bufptr + i, &len);
+           inev.ie.kind = (SINGLE_BYTE_CHAR_P (ch)
+                           ? ASCII_KEYSTROKE_EVENT
+                           : MULTIBYTE_CHAR_KEYSTROKE_EVENT);
+           inev.ie.code = ch;
+           evq_enqueue (&inev);
+         }
+
+       // count += nchars;
+
+       inev.ie.kind = NO_EVENT;  /* Already stored above.  */
+
+       if (keysym == GDK_KEY_VoidSymbol)
+         goto done;
+      }
+    }
+
+ done:
+  if (inev.ie.kind != NO_EVENT)
+    {
+      XSETFRAME (inev.ie.frame_or_window, f);
+      evq_enqueue (&inev);
+      // count++;
+    }
+
+  SAFE_FREE();
+
+  return TRUE;
+}
+
+static gboolean key_release_event(GtkWidget *widget, GdkEvent *event, gpointer 
*user_data)
+{
+  PGTK_TRACE("key_release_event");
+  return TRUE;
+}
+
+static gboolean configure_event(GtkWidget *widget, GdkEvent *event, gpointer 
*user_data)
+{
+  struct frame *f = pgtk_any_window_to_frame (event->configure.window);
+  if (f && widget == FRAME_GTK_OUTER_WIDGET (f)) {
+    PGTK_TRACE("%dx%d", event->configure.width, event->configure.height);
+    xg_frame_resized(f, event->configure.width, event->configure.height);
+  }
+  return TRUE;
+}
+
+static gboolean map_event(GtkWidget *widget, GdkEvent *event, gpointer 
*user_data)
+{
+  struct frame *f = pgtk_any_window_to_frame (event->any.window);
+  union buffered_input_event inev;
+
+  PGTK_TRACE("map_event");
+
+  EVENT_INIT (inev.ie);
+  inev.ie.kind = NO_EVENT;
+  inev.ie.arg = Qnil;
+
+  if (f)
+    {
+      bool iconified = FRAME_ICONIFIED_P (f);
+
+      /* Check if fullscreen was specified before we where mapped the
+        first time, i.e. from the command line.  */
+      if (!FRAME_X_OUTPUT(f)->has_been_visible)
+       {
+         set_fullscreen_state(f);
+       }
+
+      if (!iconified)
+       {
+         /* The `z-group' is reset every time a frame becomes
+            invisible.  Handle this here.  */
+         if (FRAME_Z_GROUP (f) == z_group_above)
+           x_set_z_group (f, Qabove, Qnil);
+         else if (FRAME_Z_GROUP (f) == z_group_below)
+           x_set_z_group (f, Qbelow, Qnil);
+       }
+
+      SET_FRAME_VISIBLE (f, 1);
+      SET_FRAME_ICONIFIED (f, false);
+      FRAME_X_OUTPUT(f)->has_been_visible = true;
+
+      if (iconified)
+       {
+         inev.ie.kind = DEICONIFY_EVENT;
+         XSETFRAME (inev.ie.frame_or_window, f);
+       }
+      else if (! NILP (Vframe_list) && ! NILP (XCDR (Vframe_list)))
+       /* Force a redisplay sooner or later to update the
+          frame titles in case this is the second frame.  */
+       record_asynch_buffer_change ();
+    }
+
+  if (inev.ie.kind != NO_EVENT)
+    evq_enqueue(&inev);
+  return TRUE;
+}
+
+static gboolean window_state_event(GtkWidget *widget, GdkEvent *event, 
gpointer *user_data)
+{
+  struct frame *f = pgtk_any_window_to_frame (event->window_state.window);
+  union buffered_input_event inev;
+
+  EVENT_INIT (inev.ie);
+  inev.ie.kind = NO_EVENT;
+  inev.ie.arg = Qnil;
+
+  if (f) {
+    if (event->window_state.new_window_state & GDK_WINDOW_STATE_FOCUSED)
+      {
+       if (FRAME_ICONIFIED_P (f))
+         {
+           /* Gnome shell does not iconify us when C-z is pressed.
+              It hides the frame.  So if our state says we aren't
+              hidden anymore, treat it as deiconified.  */
+           SET_FRAME_VISIBLE (f, 1);
+           SET_FRAME_ICONIFIED (f, false);
+           FRAME_X_OUTPUT(f)->has_been_visible = true;
+           inev.ie.kind = DEICONIFY_EVENT;
+           XSETFRAME (inev.ie.frame_or_window, f);
+         }
+      }
+  }
+
+  if (inev.ie.kind != NO_EVENT)
+    evq_enqueue(&inev);
+  return TRUE;
+}
+
+static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer 
*user_data)
+{
+  struct frame *f = pgtk_any_window_to_frame (event->any.window);
+  union buffered_input_event inev;
+
+  PGTK_TRACE("delete_event");
+
+  EVENT_INIT (inev.ie);
+  inev.ie.kind = NO_EVENT;
+  inev.ie.arg = Qnil;
+
+  if (f) {
+    inev.ie.kind = DELETE_WINDOW_EVENT;
+    XSETFRAME (inev.ie.frame_or_window, f);
+  }
+
+  if (inev.ie.kind != NO_EVENT)
+    evq_enqueue(&inev);
+  return TRUE;
+}
+
+void
+pgtk_focus_frame (struct frame *f, bool noactivate)
+{
+  struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+
+  GtkWidget *wid = FRAME_GTK_OUTER_WIDGET(f);
+
+  if (dpyinfo->x_focus_frame != f)
+    {
+      block_input ();
+      gtk_window_present (GTK_WINDOW (wid));
+      unblock_input ();
+    }
+}
+
+
+static void
+frame_highlight (struct frame *f)
+{
+  /* We used to only do this if Vx_no_window_manager was non-nil, but
+     the ICCCM (section 4.1.6) says that the window's border pixmap
+     and border pixel are window attributes which are "private to the
+     client", so we can always change it to whatever we want.  */
+  block_input ();
+  /* I recently started to get errors in this XSetWindowBorder, depending on
+     the window-manager in use, tho something more is at play since I've been
+     using that same window-manager binary for ever.  Let's not crash just
+     because of this (bug#9310).  */
+#if 0
+  x_catch_errors (FRAME_X_DISPLAY (f));
+  XSetWindowBorder (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                   FRAME_X_OUTPUT(f)->border_pixel);
+  x_uncatch_errors ();
+#endif
+  unblock_input ();
+  x_update_cursor (f, true);
+#if 0
+  x_set_frame_alpha (f);
+#endif
+}
+
+static void
+frame_unhighlight (struct frame *f)
+{
+  /* We used to only do this if Vx_no_window_manager was non-nil, but
+     the ICCCM (section 4.1.6) says that the window's border pixmap
+     and border pixel are window attributes which are "private to the
+     client", so we can always change it to whatever we want.  */
+  block_input ();
+  /* Same as above for XSetWindowBorder (bug#9310).  */
+#if 0
+  x_catch_errors (FRAME_X_DISPLAY (f));
+  XSetWindowBorderPixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                         FRAME_X_OUTPUT(f)->border_tile);
+  x_uncatch_errors ();
+#endif
+  unblock_input ();
+  x_update_cursor (f, true);
+#if 0
+  x_set_frame_alpha (f);
+#endif
+}
+
+
+static void
+x_frame_rehighlight (struct pgtk_display_info *dpyinfo)
+{
+  struct frame *old_highlight = dpyinfo->x_highlight_frame;
+
+  if (dpyinfo->x_focus_frame)
+    {
+      dpyinfo->x_highlight_frame
+       = ((FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame)))
+          ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->x_focus_frame))
+          : dpyinfo->x_focus_frame);
+      if (! FRAME_LIVE_P (dpyinfo->x_highlight_frame))
+       {
+         fset_focus_frame (dpyinfo->x_focus_frame, Qnil);
+         dpyinfo->x_highlight_frame = dpyinfo->x_focus_frame;
+       }
+    }
+  else
+    dpyinfo->x_highlight_frame = 0;
+
+  if (dpyinfo->x_highlight_frame != old_highlight)
+    {
+      if (old_highlight)
+       frame_unhighlight (old_highlight);
+      if (dpyinfo->x_highlight_frame)
+       frame_highlight (dpyinfo->x_highlight_frame);
+    }
+}
+
+/* The focus has changed.  Update the frames as necessary to reflect
+   the new situation.  Note that we can't change the selected frame
+   here, because the Lisp code we are interrupting might become confused.
+   Each event gets marked with the frame in which it occurred, so the
+   Lisp code can tell when the switch took place by examining the events.  */
+
+static void
+x_new_focus_frame (struct pgtk_display_info *dpyinfo, struct frame *frame)
+{
+  struct frame *old_focus = dpyinfo->x_focus_frame;
+
+  if (frame != dpyinfo->x_focus_frame)
+    {
+      /* Set this before calling other routines, so that they see
+        the correct value of x_focus_frame.  */
+      dpyinfo->x_focus_frame = frame;
+
+#if 0
+      if (old_focus && old_focus->auto_lower)
+       x_lower_frame (old_focus);
+#endif
+
+#if 0
+      if (dpyinfo->x_focus_frame && dpyinfo->x_focus_frame->auto_raise)
+       dpyinfo->x_pending_autoraise_frame = dpyinfo->x_focus_frame;
+      else
+       dpyinfo->x_pending_autoraise_frame = NULL;
+#endif
+    }
+
+  x_frame_rehighlight (dpyinfo);
+}
+
+/* The focus may have changed.  Figure out if it is a real focus change,
+   by checking both FocusIn/Out and Enter/LeaveNotify events.
+
+   Returns FOCUS_IN_EVENT event in *BUFP. */
+
+/* Handle FocusIn and FocusOut state changes for FRAME.
+   If FRAME has focus and there exists more than one frame, puts
+   a FOCUS_IN_EVENT into *BUFP.  */
+
+static void
+x_focus_changed (gboolean is_enter, int state, struct pgtk_display_info 
*dpyinfo, struct frame *frame, union buffered_input_event *bufp)
+{
+  if (is_enter)
+    {
+      if (dpyinfo->x_focus_event_frame != frame)
+        {
+          x_new_focus_frame (dpyinfo, frame);
+          dpyinfo->x_focus_event_frame = frame;
+
+          /* Don't stop displaying the initial startup message
+             for a switch-frame event we don't need.  */
+          /* When run as a daemon, Vterminal_frame is always NIL.  */
+          bufp->ie.arg = (((NILP (Vterminal_frame)
+                         || ! FRAME_PGTK_P (XFRAME (Vterminal_frame))
+                         || EQ (Fdaemonp (), Qt))
+                       && CONSP (Vframe_list)
+                       && !NILP (XCDR (Vframe_list)))
+                      ? Qt : Qnil);
+          bufp->ie.kind = FOCUS_IN_EVENT;
+          XSETFRAME (bufp->ie.frame_or_window, frame);
+        }
+
+      frame->output_data.pgtk->focus_state |= state;
+
+    }
+  else
+    {
+      frame->output_data.pgtk->focus_state &= ~state;
+
+      if (dpyinfo->x_focus_event_frame == frame)
+        {
+          dpyinfo->x_focus_event_frame = 0;
+          x_new_focus_frame (dpyinfo, 0);
+
+          bufp->ie.kind = FOCUS_OUT_EVENT;
+          XSETFRAME (bufp->ie.frame_or_window, frame);
+        }
+
+#if 0
+      if (frame->pointer_invisible)
+        XTtoggle_invisible_pointer (frame, false);
+#endif
+    }
+}
+
+static gboolean
+enter_notify_event(GtkWidget *widget, GdkEvent *event, gpointer *user_data)
+{
+  PGTK_TRACE("enter_notify_event");
+  union buffered_input_event inev;
+  struct frame *focus_frame = 
pgtk_any_window_to_frame(gtk_widget_get_window(widget));
+  int focus_state
+    = focus_frame ? focus_frame->output_data.pgtk->focus_state : 0;
+
+  EVENT_INIT (inev.ie);
+  inev.ie.kind = NO_EVENT;
+  inev.ie.arg = Qnil;
+
+  if (!(focus_state & FOCUS_EXPLICIT))
+    x_focus_changed (TRUE,
+                    FOCUS_IMPLICIT,
+                    FRAME_DISPLAY_INFO(focus_frame), focus_frame, &inev);
+  if (inev.ie.kind != NO_EVENT)
+    evq_enqueue (&inev);
+  return TRUE;
+}
+
+static gboolean
+leave_notify_event(GtkWidget *widget, GdkEvent *event, gpointer *user_data)
+{
+  PGTK_TRACE("leave_notify_event");
+  union buffered_input_event inev;
+  struct frame *focus_frame = 
pgtk_any_window_to_frame(gtk_widget_get_window(widget));
+  int focus_state
+    = focus_frame ? focus_frame->output_data.pgtk->focus_state : 0;
+
+  EVENT_INIT (inev.ie);
+  inev.ie.kind = NO_EVENT;
+  inev.ie.arg = Qnil;
+
+  if (!(focus_state & FOCUS_EXPLICIT))
+    x_focus_changed (FALSE,
+                    FOCUS_IMPLICIT,
+                    FRAME_DISPLAY_INFO(focus_frame), focus_frame, &inev);
+  if (inev.ie.kind != NO_EVENT)
+    evq_enqueue (&inev);
+  return TRUE;
+}
+
+static gboolean
+focus_in_event(GtkWidget *widget, GdkEvent *event, gpointer *user_data)
+{
+  PGTK_TRACE("focus_in_event");
+  union buffered_input_event inev;
+  struct frame *frame = 
pgtk_any_window_to_frame(gtk_widget_get_window(widget));
+
+  if (frame == NULL)
+    return TRUE;
+
+  EVENT_INIT (inev.ie);
+  inev.ie.kind = NO_EVENT;
+  inev.ie.arg = Qnil;
+
+  x_focus_changed (TRUE, FOCUS_IMPLICIT,
+                  FRAME_DISPLAY_INFO(frame), frame, &inev);
+  if (inev.ie.kind != NO_EVENT)
+    evq_enqueue (&inev);
+  return TRUE;
+}
+
+static gboolean
+focus_out_event(GtkWidget *widget, GdkEvent *event, gpointer *user_data)
+{
+  PGTK_TRACE("focus_out_event");
+  union buffered_input_event inev;
+  struct frame *frame = 
pgtk_any_window_to_frame(gtk_widget_get_window(widget));
+
+  if (frame == NULL)
+    return TRUE;
+
+  EVENT_INIT (inev.ie);
+  inev.ie.kind = NO_EVENT;
+  inev.ie.arg = Qnil;
+
+  x_focus_changed (FALSE, FOCUS_IMPLICIT,
+                  FRAME_DISPLAY_INFO(frame), frame, &inev);
+  if (inev.ie.kind != NO_EVENT)
+    evq_enqueue(&inev);
+  return TRUE;
+}
+
+/* Function to report a mouse movement to the mainstream Emacs code.
+   The input handler calls this.
+
+   We have received a mouse movement event, which is given in *event.
+   If the mouse is over a different glyph than it was last time, tell
+   the mainstream emacs code by setting mouse_moved.  If not, ask for
+   another motion event, so we can check again the next time it moves.  */
+
+static bool
+note_mouse_movement (struct frame *frame, const GdkEventMotion *event)
+{
+  XRectangle *r;
+  struct pgtk_display_info *dpyinfo;
+
+  if (!FRAME_X_OUTPUT (frame))
+    return false;
+
+  dpyinfo = FRAME_DISPLAY_INFO (frame);
+  dpyinfo->last_mouse_movement_time = event->time;
+  dpyinfo->last_mouse_motion_frame = frame;
+  dpyinfo->last_mouse_motion_x = event->x;
+  dpyinfo->last_mouse_motion_y = event->y;
+
+  if (event->window != gtk_widget_get_window(FRAME_GTK_WIDGET (frame)))
+    {
+      frame->mouse_moved = true;
+      dpyinfo->last_mouse_scroll_bar = NULL;
+      note_mouse_highlight (frame, -1, -1);
+      dpyinfo->last_mouse_glyph_frame = NULL;
+      return true;
+    }
+
+
+  /* Has the mouse moved off the glyph it was on at the last sighting?  */
+  r = &dpyinfo->last_mouse_glyph;
+  if (frame != dpyinfo->last_mouse_glyph_frame
+      || event->x < r->x || event->x >= r->x + r->width
+      || event->y < r->y || event->y >= r->y + r->height)
+    {
+      frame->mouse_moved = true;
+      dpyinfo->last_mouse_scroll_bar = NULL;
+      note_mouse_highlight (frame, event->x, event->y);
+      /* Remember which glyph we're now on.  */
+      remember_mouse_glyph (frame, event->x, event->y, r);
+      dpyinfo->last_mouse_glyph_frame = frame;
+      return true;
+    }
+
+  return false;
+}
+
+static gboolean
+motion_notify_event(GtkWidget *widget, GdkEvent *event, gpointer *user_data)
+{
+  PGTK_TRACE("motion_notify_event");
+  union buffered_input_event inev;
+  struct frame *f, *frame;
+  struct pgtk_display_info *dpyinfo;
+  Mouse_HLInfo *hlinfo;
+
+  EVENT_INIT (inev.ie);
+  inev.ie.kind = NO_EVENT;
+  inev.ie.arg = Qnil;
+
+  previous_help_echo_string = help_echo_string;
+  help_echo_string = Qnil;
+
+  frame = pgtk_any_window_to_frame(gtk_widget_get_window(widget));
+  dpyinfo = FRAME_DISPLAY_INFO (frame);
+  f = (x_mouse_grabbed (dpyinfo) ? dpyinfo->last_mouse_frame
+       : pgtk_any_window_to_frame(gtk_widget_get_window(widget)));
+  hlinfo = MOUSE_HL_INFO (f);
+
+  if (hlinfo->mouse_face_hidden)
+    {
+      hlinfo->mouse_face_hidden = false;
+      clear_mouse_face (hlinfo);
+    }
+
+  if (f && xg_event_is_for_scrollbar (f, event))
+    f = 0;
+  if (f)
+    {
+      /* Maybe generate a SELECT_WINDOW_EVENT for
+        `mouse-autoselect-window' but don't let popup menus
+        interfere with this (Bug#1261).  */
+      if (!NILP (Vmouse_autoselect_window)
+         /* Don't switch if we're currently in the minibuffer.
+            This tries to work around problems where the
+            minibuffer gets unselected unexpectedly, and where
+            you then have to move your mouse all the way down to
+            the minibuffer to select it.  */
+         && !MINI_WINDOW_P (XWINDOW (selected_window))
+         /* With `focus-follows-mouse' non-nil create an event
+            also when the target window is on another frame.  */
+         && (f == XFRAME (selected_frame)
+             || !NILP (focus_follows_mouse)))
+       {
+         static Lisp_Object last_mouse_window;
+         Lisp_Object window = window_from_coordinates
+           (f, event->motion.x, event->motion.y, 0, false);
+
+         /* A window will be autoselected only when it is not
+            selected now and the last mouse movement event was
+            not in it.  The remainder of the code is a bit vague
+            wrt what a "window" is.  For immediate autoselection,
+            the window is usually the entire window but for GTK
+            where the scroll bars don't count.  For delayed
+            autoselection the window is usually the window's text
+            area including the margins.  */
+         if (WINDOWP (window)
+             && !EQ (window, last_mouse_window)
+             && !EQ (window, selected_window))
+           {
+             inev.ie.kind = SELECT_WINDOW_EVENT;
+             inev.ie.frame_or_window = window;
+           }
+
+         /* Remember the last window where we saw the mouse.  */
+         last_mouse_window = window;
+       }
+
+      if (!note_mouse_movement (f, &event->motion))
+       help_echo_string = previous_help_echo_string;
+    }
+  else
+    {
+      /* If we move outside the frame, then we're
+        certainly no longer on any text in the frame.  */
+      clear_mouse_face (hlinfo);
+    }
+
+  /* If the contents of the global variable help_echo_string
+     has changed, generate a HELP_EVENT.  */
+  int do_help = 0;
+  if (!NILP (help_echo_string)
+      || !NILP (previous_help_echo_string))
+    do_help = 1;
+
+  if (inev.ie.kind != NO_EVENT)
+    evq_enqueue (&inev);
+
+  if (do_help > 0) {
+    Lisp_Object frame;
+    union buffered_input_event inev;
+
+    if (f)
+      XSETFRAME (frame, f);
+    else
+      frame = Qnil;
+
+    inev.ie.kind = HELP_EVENT;
+    inev.ie.frame_or_window = frame;
+    inev.ie.arg = help_echo_object;
+    inev.ie.x = help_echo_window;
+    inev.ie.y = help_echo_string;
+    inev.ie.timestamp = help_echo_pos;
+    evq_enqueue(&inev);
+  }
+
+  return TRUE;
+}
+
+/* Mouse clicks and mouse movement.  Rah.
+
+   Formerly, we used PointerMotionHintMask (in standard_event_mask)
+   so that we would have to call XQueryPointer after each MotionNotify
+   event to ask for another such event.  However, this made mouse tracking
+   slow, and there was a bug that made it eventually stop.
+
+   Simply asking for MotionNotify all the time seems to work better.
+
+   In order to avoid asking for motion events and then throwing most
+   of them away or busy-polling the server for mouse positions, we ask
+   the server for pointer motion hints.  This means that we get only
+   one event per group of mouse movements.  "Groups" are delimited by
+   other kinds of events (focus changes and button clicks, for
+   example), or by XQueryPointer calls; when one of these happens, we
+   get another MotionNotify event the next time the mouse moves.  This
+   is at least as efficient as getting motion events when mouse
+   tracking is on, and I suspect only negligibly worse when tracking
+   is off.  */
+
+/* Prepare a mouse-event in *RESULT for placement in the input queue.
+
+   If the event is a button press, then note that we have grabbed
+   the mouse.  */
+
+static Lisp_Object
+construct_mouse_click (struct input_event *result,
+                      const GdkEventButton *event,
+                      struct frame *f)
+{
+  /* Make the event type NO_EVENT; we'll change that when we decide
+     otherwise.  */
+  result->kind = MOUSE_CLICK_EVENT;
+  result->code = event->button - 1;
+  result->timestamp = event->time;
+  result->modifiers = (pgtk_gtk_to_emacs_modifiers (event->state)
+                      | (event->type == GDK_BUTTON_RELEASE
+                         ? up_modifier
+                         : down_modifier));
+
+  XSETINT (result->x, event->x);
+  XSETINT (result->y, event->y);
+  XSETFRAME (result->frame_or_window, f);
+  result->arg = Qnil;
+  return Qnil;
+}
+
+static gboolean
+button_event(GtkWidget *widget, GdkEvent *event, gpointer *user_data)
+{
+  PGTK_TRACE("button_event: type=%d, button=%u.", event->button.type, 
event->button.button);
+  union buffered_input_event inev;
+  struct frame *f, *frame;
+  struct pgtk_display_info *dpyinfo;
+
+  /* If we decide we want to generate an event to be seen
+     by the rest of Emacs, we put it here.  */
+  bool tool_bar_p = false;
+
+  EVENT_INIT (inev.ie);
+  inev.ie.kind = NO_EVENT;
+  inev.ie.arg = Qnil;
+
+  /* ignore double click and triple click. */
+  if (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE)
+    return TRUE;
+
+  frame = pgtk_any_window_to_frame(gtk_widget_get_window(widget));
+  dpyinfo = FRAME_DISPLAY_INFO (frame);
+
+  dpyinfo->last_mouse_glyph_frame = NULL;
+#if 0
+  x_display_set_last_user_time (dpyinfo, event->button.time);
+#endif
+
+  if (x_mouse_grabbed (dpyinfo))
+    f = dpyinfo->last_mouse_frame;
+  else
+    {
+      f = pgtk_any_window_to_frame(gtk_widget_get_window(widget));
+
+      if (f && event->button.type == GDK_BUTTON_PRESS
+         && !FRAME_NO_ACCEPT_FOCUS (f))
+       {
+         /* When clicking into a child frame or when clicking
+            into a parent frame with the child frame selected and
+            `no-accept-focus' is not set, select the clicked
+            frame.  */
+         struct frame *hf = dpyinfo->x_highlight_frame;
+
+         if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf)))
+           {
+             block_input ();
+#if 0
+             XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
+                             RevertToParent, CurrentTime);
+             if (FRAME_PARENT_FRAME (f))
+               XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f));
+#endif
+             unblock_input ();
+           }
+       }
+    }
+
+  if (f && xg_event_is_for_scrollbar (f, event))
+    f = 0;
+  if (f)
+    {
+      if (!tool_bar_p)
+       {
+         if (ignore_next_mouse_click_timeout)
+           {
+             if (event->type == GDK_BUTTON_PRESS
+                 && event->button.time > ignore_next_mouse_click_timeout)
+               {
+                 ignore_next_mouse_click_timeout = 0;
+                 construct_mouse_click (&inev.ie, &event->button, f);
+               }
+             if (event->type == GDK_BUTTON_RELEASE)
+               ignore_next_mouse_click_timeout = 0;
+           }
+         else
+           construct_mouse_click (&inev.ie, &event->button, f);
+       }
+#if 0
+      if (FRAME_X_EMBEDDED_P (f))
+       xembed_send_message (f, event->button.time,
+                            XEMBED_REQUEST_FOCUS, 0, 0, 0);
+#endif
+    }
+
+  if (event->type == GDK_BUTTON_PRESS)
+    {
+      dpyinfo->grabbed |= (1 << event->button.button);
+      dpyinfo->last_mouse_frame = f;
+    }
+  else
+    dpyinfo->grabbed &= ~(1 << event->button.button);
+
+  /* Ignore any mouse motion that happened before this event;
+     any subsequent mouse-movement Emacs events should reflect
+     only motion after the ButtonPress/Release.  */
+  if (f != 0)
+    f->mouse_moved = false;
+
+  if (inev.ie.kind != NO_EVENT)
+    evq_enqueue (&inev);
+  return TRUE;
+}
+
+static gboolean
+scroll_event(GtkWidget *widget, GdkEvent *event, gpointer *user_data)
+{
+  PGTK_TRACE("scroll_event");
+  union buffered_input_event inev;
+  struct frame *f, *frame;
+  struct pgtk_display_info *dpyinfo;
+
+  EVENT_INIT (inev.ie);
+  inev.ie.kind = NO_EVENT;
+  inev.ie.arg = Qnil;
+
+  frame = pgtk_any_window_to_frame(gtk_widget_get_window(widget));
+  dpyinfo = FRAME_DISPLAY_INFO (frame);
+
+  if (x_mouse_grabbed (dpyinfo))
+    f = dpyinfo->last_mouse_frame;
+  else
+    f = pgtk_any_window_to_frame(gtk_widget_get_window(widget));
+
+  inev.ie.kind = WHEEL_EVENT;
+  inev.ie.timestamp = event->scroll.time;
+  inev.ie.modifiers = pgtk_gtk_to_emacs_modifiers (event->scroll.state);
+  XSETINT (inev.ie.x, event->scroll.x);
+  XSETINT (inev.ie.y, event->scroll.y);
+  XSETFRAME (inev.ie.frame_or_window, f);
+  inev.ie.arg = Qnil;
+
+  switch (event->scroll.direction) {
+  case GDK_SCROLL_UP:
+    inev.ie.kind = WHEEL_EVENT;
+    inev.ie.modifiers |= up_modifier;
+    break;
+  case GDK_SCROLL_DOWN:
+    inev.ie.kind = WHEEL_EVENT;
+    inev.ie.modifiers |= down_modifier;
+    break;
+  case GDK_SCROLL_LEFT:
+    inev.ie.kind = HORIZ_WHEEL_EVENT;
+    inev.ie.modifiers |= up_modifier;
+    break;
+  case GDK_SCROLL_RIGHT:
+    inev.ie.kind = HORIZ_WHEEL_EVENT;
+    inev.ie.modifiers |= down_modifier;
+    break;
+  case GDK_SCROLL_SMOOTH:
+    if (event->scroll.delta_y >= 0.5) {
+      inev.ie.kind = WHEEL_EVENT;
+      inev.ie.modifiers |= down_modifier;
+    } else if (event->scroll.delta_y <= -0.5) {
+      inev.ie.kind = WHEEL_EVENT;
+      inev.ie.modifiers |= up_modifier;
+    } else if (event->scroll.delta_x >= 0.5) {
+      inev.ie.kind = HORIZ_WHEEL_EVENT;
+      inev.ie.modifiers |= down_modifier;
+    } else if (event->scroll.delta_x <= -0.5) {
+      inev.ie.kind = HORIZ_WHEEL_EVENT;
+      inev.ie.modifiers |= up_modifier;
+    } else
+      return TRUE;
+    break;
+  default:
+    return TRUE;
+  }
+
+  if (inev.ie.kind != NO_EVENT)
+    evq_enqueue (&inev);
+  return TRUE;
+}
+
+static gboolean drag_drop(GtkWidget *widget,
+                         GdkDragContext *context,
+                         gint x, gint y,
+                         guint time_,
+                         gpointer user_data)
+{
+  PGTK_TRACE("drag_drop");
+  GdkAtom target = gtk_drag_dest_find_target(widget, context, NULL);
+  PGTK_TRACE("drag_drop: target: %p", (void *) target);
+
+  if (target == GDK_NONE) {
+    gtk_drag_finish(context, TRUE, FALSE, time_);
+    return FALSE;
+  }
+
+  gtk_drag_get_data(widget, context, target, time_);
+
+  return TRUE;
+}
+
+static void drag_data_received(GtkWidget *widget, GdkDragContext *context,
+                              gint x, gint y,
+                              GtkSelectionData *data,
+                              guint info, guint time_,
+                              gpointer user_data)
+{
+  PGTK_TRACE("drag_data_received:");
+  struct frame *f = pgtk_any_window_to_frame(gtk_widget_get_window(widget));
+  gchar **uris = gtk_selection_data_get_uris(data);
+
+  if (uris != NULL) {
+    for (int i = 0; uris[i] != NULL; i++) {
+      union buffered_input_event inev;
+      Lisp_Object arg = Qnil;
+
+      PGTK_TRACE("drag_data_received: uri: %s", uris[i]);
+
+      EVENT_INIT (inev.ie);
+      inev.ie.kind = NO_EVENT;
+      inev.ie.arg = Qnil;
+
+      arg = list2(Qurl, build_string(uris[i]));
+
+      inev.ie.kind = DRAG_N_DROP_EVENT;
+      inev.ie.modifiers = 0;
+      XSETINT(inev.ie.x, x);
+      XSETINT(inev.ie.y, y);
+      XSETFRAME(inev.ie.frame_or_window, f);
+      inev.ie.arg = arg;
+      inev.ie.timestamp = 0;
+
+      evq_enqueue (&inev);
+    }
+  }
+  PGTK_TRACE("drag_data_received: that's all.");
+
+  gtk_drag_finish(context, TRUE, FALSE, time_);
+}
+
+void
+pgtk_set_event_handler(struct frame *f)
+{
+  gtk_drag_dest_set(FRAME_GTK_WIDGET(f), GTK_DEST_DEFAULT_ALL, NULL, 0, 
GDK_ACTION_COPY);
+  gtk_drag_dest_add_uri_targets(FRAME_GTK_WIDGET(f));
+
+  g_signal_connect(G_OBJECT(FRAME_GTK_OUTER_WIDGET(f)), "window-state-event", 
G_CALLBACK(window_state_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_OUTER_WIDGET(f)), "delete-event", 
G_CALLBACK(delete_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_OUTER_WIDGET(f)), "map-event", 
G_CALLBACK(map_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_OUTER_WIDGET(f)), "event", 
G_CALLBACK(pgtk_handle_event), NULL);
+
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "size-allocate", 
G_CALLBACK(size_allocate), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "key-press-event", 
G_CALLBACK(key_press_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "key-release-event", 
G_CALLBACK(key_release_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "focus-in-event", 
G_CALLBACK(focus_in_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "focus-out-event", 
G_CALLBACK(focus_out_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "enter-notify-event", 
G_CALLBACK(enter_notify_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "leave-notify-event", 
G_CALLBACK(leave_notify_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "motion-notify-event", 
G_CALLBACK(motion_notify_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "button-press-event", 
G_CALLBACK(button_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "button-release-event", 
G_CALLBACK(button_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "scroll-event", 
G_CALLBACK(scroll_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "selection-clear-event", 
G_CALLBACK(pgtk_selection_lost), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "configure-event", 
G_CALLBACK(configure_event), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "drag-drop", 
G_CALLBACK(drag_drop), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "drag-data-received", 
G_CALLBACK(drag_data_received), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "draw", 
G_CALLBACK(pgtk_handle_draw), NULL);
+  g_signal_connect(G_OBJECT(FRAME_GTK_WIDGET(f)), "event", 
G_CALLBACK(pgtk_handle_event), NULL);
+}
+
+static void
+my_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
+               const gchar *msg, gpointer user_data)
+{
+  if (!strstr (msg, "g_set_prgname"))
+      fprintf (stderr, "%s-WARNING **: %s", log_domain, msg);
+}
+
+/* Test whether two display-name strings agree up to the dot that separates
+   the screen number from the server number.  */
+static bool
+same_x_server (const char *name1, const char *name2)
+{
+  bool seen_colon = false;
+  Lisp_Object sysname = Fsystem_name ();
+  const char *system_name = SSDATA (sysname);
+  ptrdiff_t system_name_length = SBYTES (sysname);
+  ptrdiff_t length_until_period = 0;
+
+  while (system_name[length_until_period] != 0
+        && system_name[length_until_period] != '.')
+    length_until_period++;
+
+  /* Treat `unix' like an empty host name.  */
+  if (! strncmp (name1, "unix:", 5))
+    name1 += 4;
+  if (! strncmp (name2, "unix:", 5))
+    name2 += 4;
+  /* Treat this host's name like an empty host name.  */
+  if (! strncmp (name1, system_name, system_name_length)
+      && name1[system_name_length] == ':')
+    name1 += system_name_length;
+  if (! strncmp (name2, system_name, system_name_length)
+      && name2[system_name_length] == ':')
+    name2 += system_name_length;
+  /* Treat this host's domainless name like an empty host name.  */
+  if (! strncmp (name1, system_name, length_until_period)
+      && name1[length_until_period] == ':')
+    name1 += length_until_period;
+  if (! strncmp (name2, system_name, length_until_period)
+      && name2[length_until_period] == ':')
+    name2 += length_until_period;
+
+  for (; *name1 != '\0' && *name1 == *name2; name1++, name2++)
+    {
+      if (*name1 == ':')
+       seen_colon = true;
+      if (seen_colon && *name1 == '.')
+       return true;
+    }
+  return (seen_colon
+         && (*name1 == '.' || *name1 == '\0')
+         && (*name2 == '.' || *name2 == '\0'));
+}
+
+/* Open a connection to X display DISPLAY_NAME, and return
+   the structure that describes the open display.
+   If we cannot contact the display, return null.  */
+
+struct pgtk_display_info *
+pgtk_term_init (Lisp_Object display_name, char *resource_name)
+{
+  GdkDisplay *dpy;
+  struct terminal *terminal;
+  struct pgtk_display_info *dpyinfo;
+  static int x_initialized = 0;
+  static unsigned x_display_id = 0;
+
+  block_input ();
+
+  if (!x_initialized)
+    {
+      Fset_input_interrupt_mode (Qt);
+      baud_rate = 19200;
+
+#ifdef USE_CAIRO
+      gui_init_fringe (&pgtk_redisplay_interface);
+#endif
+
+      ++x_initialized;
+    }
+
+  {
+#define NUM_ARGV 10
+    int argc;
+    char *argv[NUM_ARGV];
+    char **argv2 = argv;
+    guint id;
+
+    if (x_initialized++ > 1)
+      {
+       xg_display_open (SSDATA (display_name), &dpy);
+      }
+    else
+      {
+        static char display_opt[] = "--display";
+        static char name_opt[] = "--name";
+
+        for (argc = 0; argc < NUM_ARGV; ++argc)
+          argv[argc] = 0;
+
+        argc = 0;
+        argv[argc++] = initial_argv[0];
+
+        if (strlen(SSDATA(display_name)) != 0)
+          {
+            argv[argc++] = display_opt;
+            argv[argc++] = SSDATA (display_name);
+          }
+
+        argv[argc++] = name_opt;
+        argv[argc++] = resource_name;
+
+       /* Work around GLib bug that outputs a faulty warning. See
+          https://bugzilla.gnome.org/show_bug.cgi?id=563627.  */
+       id = g_log_set_handler ("GLib", G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL
+                                 | G_LOG_FLAG_RECURSION, my_log_handler, NULL);
+
+       /* gtk_init does set_locale.  Fix locale before and after.  */
+       fixup_locale ();
+       unrequest_sigio (); /* See comment in x_display_ok.  */
+       gtk_init (&argc, &argv2);
+       request_sigio ();
+       fixup_locale ();
+
+
+        g_log_remove_handler ("GLib", id);
+
+        xg_initialize ();
+
+        dpy = DEFAULT_GDK_DISPLAY ();
+      }
+  }
+
+  /* Detect failure.  */
+  if (dpy == 0)
+    {
+      unblock_input ();
+      return 0;
+    }
+
+
+  dpyinfo = xzalloc (sizeof *dpyinfo);
+  pgtk_initialize_display_info (dpyinfo);
+  terminal = pgtk_create_terminal (dpyinfo);
+
+  {
+    struct pgtk_display_info *share;
+
+    for (share = x_display_list; share; share = share->next)
+      if (same_x_server (SSDATA (XCAR (share->name_list_element)),
+                        SSDATA (display_name)))
+       break;
+    if (share)
+      terminal->kboard = share->terminal->kboard;
+    else
+      {
+       terminal->kboard = allocate_kboard (Qpgtk);
+
+       /* Don't let the initial kboard remain current longer than necessary.
+          That would cause problems if a file loaded on startup tries to
+          prompt in the mini-buffer.  */
+       if (current_kboard == initial_kboard)
+         current_kboard = terminal->kboard;
+      }
+    terminal->kboard->reference_count++;
+  }
+
+  /* Put this display on the chain.  */
+  dpyinfo->next = x_display_list;
+  x_display_list = dpyinfo;
+
+  dpyinfo->name_list_element = Fcons (display_name, Qnil);
+  dpyinfo->gdpy = dpy;
+
+  /* https://lists.gnu.org/r/emacs-devel/2015-11/msg00194.html  */
+  dpyinfo->smallest_font_height = 1;
+  dpyinfo->smallest_char_width = 1;
+
+  /* Set the name of the terminal. */
+  terminal->name = xlispstrdup (display_name);
+
+  Lisp_Object system_name = Fsystem_name ();
+  ptrdiff_t nbytes;
+  if (INT_ADD_WRAPV (SBYTES (Vinvocation_name), SBYTES (system_name) + 2,
+                    &nbytes))
+    memory_full (SIZE_MAX);
+  dpyinfo->x_id = ++x_display_id;
+  dpyinfo->x_id_name = xmalloc (nbytes);
+  char *nametail = lispstpcpy (dpyinfo->x_id_name, Vinvocation_name);
+  *nametail++ = '@';
+  lispstpcpy (nametail, system_name);
+
+#if 0
+  /* Figure out which modifier bits mean what.  */
+  x_find_modifier_meanings (dpyinfo);
+#endif
+
+  /* Get the scroll bar cursor.  */
+  /* We must create a GTK cursor, it is required for GTK widgets.  */
+  dpyinfo->xg_cursor = xg_create_default_cursor (dpyinfo->gdpy);
+
+#if 0
+  dpyinfo->vertical_scroll_bar_cursor
+    = XCreateFontCursor (dpyinfo->display, XC_sb_v_double_arrow);
+
+  dpyinfo->horizontal_scroll_bar_cursor
+    = XCreateFontCursor (dpyinfo->display, XC_sb_h_double_arrow);
+#endif
+
+  reset_mouse_highlight (&dpyinfo->mouse_highlight);
+
+  {
+    GdkScreen *gscr = gdk_display_get_default_screen(dpyinfo->gdpy);
+    gdouble dpi = gdk_screen_get_resolution(gscr);
+    dpyinfo->resx = dpi;
+    dpyinfo->resy = dpi;
+  }
+
+#if 0
+  x_setup_pointer_blanking (dpyinfo);
+#endif
+
+  xsettings_initialize (dpyinfo);
+
+#ifdef F_SETOWN
+  fcntl (dpyinfo->connection, F_SETOWN, getpid ());
+#endif /* ! defined (F_SETOWN) */
+
+  if (interrupt_input)
+    init_sigio (dpyinfo->connection);
+
+  pgtk_selection_init();
+
+  unblock_input ();
+
+  return dpyinfo;
+}
+
+/* Get rid of display DPYINFO, deleting all frames on it,
+   and without sending any more commands to the X server.  */
+
+static void
+pgtk_delete_display (struct pgtk_display_info *dpyinfo)
+{
+  struct terminal *t;
+
+  /* Close all frames and delete the generic struct terminal for this
+     X display.  */
+  for (t = terminal_list; t; t = t->next_terminal)
+    if (t->type == output_pgtk && t->display_info.pgtk == dpyinfo)
+      {
+        delete_terminal (t);
+        break;
+      }
+
+  if (x_display_list == dpyinfo)
+    x_display_list = dpyinfo->next;
+  else
+    {
+      struct pgtk_display_info *tail;
+
+      for (tail = x_display_list; tail; tail = tail->next)
+       if (tail->next == dpyinfo)
+         tail->next = tail->next->next;
+    }
+
+  xfree (dpyinfo);
+}
+
+char *
+pgtk_xlfd_to_fontname (const char *xlfd)
+/* --------------------------------------------------------------------------
+    Convert an X font name (XLFD) to an Gtk font name.
+    Only family is used.
+    The string returned is temporarily allocated.
+   -------------------------------------------------------------------------- 
*/
+{
+  PGTK_TRACE("pgtk_xlfd_to_fontname");
+  char *name = xmalloc (180);
+
+  if (!strncmp (xlfd, "--", 2)) {
+    if (sscanf (xlfd, "--%179[^-]-", name) != 1)
+      name[0] = '\0';
+  } else {
+    if (sscanf (xlfd, "-%*[^-]-%179[^-]-", name) != 1)
+      name[0] = '\0';
+  }
+
+  /* stopgap for malformed XLFD input */
+  if (strlen (name) == 0)
+    strcpy (name, "Monospace");
+
+  PGTK_TRACE("converted '%s' to '%s'", xlfd, name);
+  return name;
+}
+
+bool
+pgtk_defined_color (struct frame *f,
+                  const char *name,
+                  Emacs_Color *color_def,
+                  bool alloc,
+                  bool makeIndex)
+/* --------------------------------------------------------------------------
+         Return true if named color found, and set color_def rgb accordingly.
+         If makeIndex and alloc are nonzero put the color in the color_table,
+         and set color_def pixel to the resulting index.
+         If makeIndex is zero, set color_def pixel to ARGB.
+         Return false if not found
+   -------------------------------------------------------------------------- 
*/
+{
+  // PGTK_TRACE("pgtk_defined_color(%s)", name);
+  int r;
+
+  block_input ();
+  r = pgtk_parse_color (name, color_def);
+  unblock_input ();
+  return r;
+}
+
+/* On frame F, translate the color name to RGB values.  Use cached
+   information, if possible.
+
+   Note that there is currently no way to clean old entries out of the
+   cache.  However, it is limited to names in the server's database,
+   and names we've actually looked up; list-colors-display is probably
+   the most color-intensive case we're likely to hit.  */
+
+int pgtk_parse_color (const char *color_name, Emacs_Color *color)
+{
+  // PGTK_TRACE("pgtk_parse_color: %s", color_name);
+
+  GdkRGBA rgba;
+  if (gdk_rgba_parse(&rgba, color_name)) {
+    color->red = rgba.red * 65535;
+    color->green = rgba.green * 65535;
+    color->blue = rgba.blue * 65535;
+    color->pixel =
+      (unsigned long) 0xff << 24 |
+      (color->red >> 8) << 16 |
+      (color->green >> 8) << 8 |
+      (color->blue >> 8) << 0;
+    return 1;
+  }
+  return 0;
+}
+
+int
+pgtk_lisp_to_color (Lisp_Object color, Emacs_Color *col)
+/* --------------------------------------------------------------------------
+     Convert a Lisp string object to a NS color
+   -------------------------------------------------------------------------- 
*/
+{
+  PGTK_TRACE("pgtk_lisp_to_color");
+  if (STRINGP (color))
+    return !pgtk_parse_color (SSDATA (color), col);
+  else if (SYMBOLP (color))
+    return !pgtk_parse_color (SSDATA (SYMBOL_NAME (color)), col);
+  return 1;
+}
+
+/* On frame F, translate pixel colors to RGB values for the NCOLORS
+   colors in COLORS.  On W32, we no longer try to map colors to
+   a palette.  */
+void
+pgtk_query_colors (struct frame *f, Emacs_Color *colors, int ncolors)
+{
+  PGTK_TRACE("pgtk_query_colors");
+  int i;
+
+  for (i = 0; i < ncolors; i++)
+    {
+      unsigned long pixel = colors[i].pixel;
+      /* Convert to a 16 bit value in range 0 - 0xffff. */
+#define GetRValue(p) (((p) >> 16) & 0xff)
+#define GetGValue(p) (((p) >> 8) & 0xff)
+#define GetBValue(p) (((p) >> 0) & 0xff)
+      colors[i].red = GetRValue (pixel) * 257;
+      colors[i].green = GetGValue (pixel) * 257;
+      colors[i].blue = GetBValue (pixel) * 257;
+    }
+}
+
+void
+pgtk_query_color (struct frame *f, Emacs_Color *color)
+{
+  PGTK_TRACE("pgtk_query_color");
+  pgtk_query_colors (f, color, 1);
+}
+
+void
+pgtk_clear_area (struct frame *f, int x, int y, int width, int height)
+{
+  PGTK_TRACE("pgtk_clear_area: %dx%d+%d+%d.", width, height, x, y);
+  cairo_t *cr;
+
+  eassert (width > 0 && height > 0);
+
+  cr = pgtk_begin_cr_clip (f);
+  PGTK_TRACE("back color %08lx.", (unsigned long) 
FRAME_X_OUTPUT(f)->background_color);
+  pgtk_set_cr_source_with_color (f, FRAME_X_OUTPUT(f)->background_color);
+  cairo_rectangle (cr, x, y, width, height);
+  cairo_fill (cr);
+  pgtk_end_cr_clip (f);
+}
+
+
+void
+syms_of_pgtkterm (void)
+{
+  /* from 23+ we need to tell emacs what modifiers there are.. */
+  DEFSYM (Qmodifier_value, "modifier-value");
+  DEFSYM (Qalt, "alt");
+  DEFSYM (Qhyper, "hyper");
+  DEFSYM (Qmeta, "meta");
+  DEFSYM (Qsuper, "super");
+  DEFSYM (Qcontrol, "control");
+  DEFSYM (QUTF8_STRING, "UTF8_STRING");
+
+  DEFSYM (Qfile, "file");
+  DEFSYM (Qurl, "url");
+
+  DEFSYM (Qlatin_1, "latin-1");
+
+  Fput (Qalt, Qmodifier_value, make_fixnum (alt_modifier));
+  Fput (Qhyper, Qmodifier_value, make_fixnum (hyper_modifier));
+  Fput (Qmeta, Qmodifier_value, make_fixnum (meta_modifier));
+  Fput (Qsuper, Qmodifier_value, make_fixnum (super_modifier));
+  Fput (Qcontrol, Qmodifier_value, make_fixnum (ctrl_modifier));
+
+  DEFVAR_LISP ("x-ctrl-keysym", Vx_ctrl_keysym,
+    doc: /* Which keys Emacs uses for the ctrl modifier.
+This should be one of the symbols `ctrl', `alt', `hyper', `meta',
+`super'.  For example, `ctrl' means use the Ctrl_L and Ctrl_R keysyms.
+The default is nil, which is the same as `ctrl'.  */);
+  Vx_ctrl_keysym = Qnil;
+
+  DEFVAR_LISP ("x-alt-keysym", Vx_alt_keysym,
+    doc: /* Which keys Emacs uses for the alt modifier.
+This should be one of the symbols `ctrl', `alt', `hyper', `meta',
+`super'.  For example, `alt' means use the Alt_L and Alt_R keysyms.
+The default is nil, which is the same as `alt'.  */);
+  Vx_alt_keysym = Qnil;
+
+  DEFVAR_LISP ("x-hyper-keysym", Vx_hyper_keysym,
+    doc: /* Which keys Emacs uses for the hyper modifier.
+This should be one of the symbols `ctrl', `alt', `hyper', `meta',
+`super'.  For example, `hyper' means use the Hyper_L and Hyper_R
+keysyms.  The default is nil, which is the same as `hyper'.  */);
+  Vx_hyper_keysym = Qnil;
+
+  DEFVAR_LISP ("x-meta-keysym", Vx_meta_keysym,
+    doc: /* Which keys Emacs uses for the meta modifier.
+This should be one of the symbols `ctrl', `alt', `hyper', `meta',
+`super'.  For example, `meta' means use the Meta_L and Meta_R keysyms.
+The default is nil, which is the same as `meta'.  */);
+  Vx_meta_keysym = Qnil;
+
+  DEFVAR_LISP ("x-super-keysym", Vx_super_keysym,
+    doc: /* Which keys Emacs uses for the super modifier.
+This should be one of the symbols `ctrl', `alt', `hyper', `meta',
+`super'.  For example, `super' means use the Super_L and Super_R
+keysyms.  The default is nil, which is the same as `super'.  */);
+  Vx_super_keysym = Qnil;
+
+  /* TODO: move to common code */
+  DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
+              doc: /* Which toolkit scroll bars Emacs uses, if any.
+A value of nil means Emacs doesn't use toolkit scroll bars.
+With the X Window system, the value is a symbol describing the
+X toolkit.  Possible values are: gtk, motif, xaw, or xaw3d.
+With MS Windows or Nextstep, the value is t.  */);
+  // Vx_toolkit_scroll_bars = Qt;
+  Vx_toolkit_scroll_bars = intern_c_string ("gtk");
+
+  DEFVAR_BOOL ("x-use-underline-position-properties",
+              x_use_underline_position_properties,
+     doc: /*Non-nil means make use of UNDERLINE_POSITION font properties.
+A value of nil means ignore them.  If you encounter fonts with bogus
+UNDERLINE_POSITION font properties, for example 7x13 on XFree prior
+to 4.1, set this to nil. */);
+  x_use_underline_position_properties = 0;
+
+  DEFVAR_BOOL ("x-underline-at-descent-line",
+              x_underline_at_descent_line,
+     doc: /* Non-nil means to draw the underline at the same place as the 
descent line.
+A value of nil means to draw the underline according to the value of the
+variable `x-use-underline-position-properties', which is usually at the
+baseline level.  The default value is nil.  */);
+  x_underline_at_descent_line = 0;
+
+  DEFVAR_BOOL ("x-gtk-use-window-move", x_gtk_use_window_move,
+    doc: /* Non-nil means rely on gtk_window_move to set frame positions.
+If this variable is t (the default), the GTK build uses the function
+gtk_window_move to set or store frame positions and disables some time
+consuming frame position adjustments.  In newer versions of GTK, Emacs
+always uses gtk_window_move and ignores the value of this variable.  */);
+  x_gtk_use_window_move = true;
+
+  DEFSYM (Qx_gtk_map_stock, "x-gtk-map-stock");
+
+  DEFVAR_LISP ("pgtk-wait-for-event-timeout", Vpgtk_wait_for_event_timeout,
+    doc: /* How long to wait for X events.
+
+Emacs will wait up to this many seconds to receive X events after
+making changes which affect the state of the graphical interface.
+Under some window managers this can take an indefinite amount of time,
+so it is important to limit the wait.
+
+If set to a non-float value, there will be no wait at all.  */);
+  Vpgtk_wait_for_event_timeout = make_float (0.1);
+
+  DEFVAR_LISP ("pgtk-keysym-table", Vpgtk_keysym_table,
+    doc: /* Hash table of character codes indexed by X keysym codes.  */);
+  Vpgtk_keysym_table = make_hash_table (hashtest_eql, 900,
+                                       DEFAULT_REHASH_SIZE,
+                                       DEFAULT_REHASH_THRESHOLD,
+                                       Qnil, false);
+
+  window_being_scrolled = Qnil;
+  staticpro(&window_being_scrolled);
+
+  /* Tell Emacs about this window system.  */
+  Fprovide (Qpgtk, Qnil);
+
+}
+
+cairo_t *
+pgtk_begin_cr_clip (struct frame *f)
+{
+  cairo_t *cr = FRAME_CR_CONTEXT (f);
+
+  PGTK_TRACE("pgtk_begin_cr_clip");
+  if (! FRAME_CR_SURFACE (f))
+    {
+      FRAME_CR_SURFACE(f) = 
gdk_window_create_similar_surface(gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
+                                                             
CAIRO_CONTENT_COLOR_ALPHA,
+                                                             FRAME_PIXEL_WIDTH 
(f),
+                                                             
FRAME_PIXEL_HEIGHT (f));
+    }
+
+  if (!cr)
+    {
+      cr = cairo_create (FRAME_CR_SURFACE (f));
+      FRAME_CR_CONTEXT (f) = cr;
+    }
+
+  cairo_save (cr);
+
+  return cr;
+}
+
+void
+pgtk_end_cr_clip (struct frame *f)
+{
+  PGTK_TRACE("pgtk_end_cr_clip");
+  cairo_restore (FRAME_CR_CONTEXT (f));
+
+  GtkWidget *widget = FRAME_GTK_WIDGET(f);
+  gtk_widget_queue_draw(widget);
+}
+
+void
+pgtk_set_cr_source_with_gc_foreground (struct frame *f, XGCValues *gc)
+{
+  PGTK_TRACE("pgtk_set_cr_source_with_gc_foreground: %08lx", gc->foreground);
+  pgtk_set_cr_source_with_color(f, gc->foreground);
+}
+
+void
+pgtk_set_cr_source_with_gc_background (struct frame *f, XGCValues *gc)
+{
+  PGTK_TRACE("pgtk_set_cr_source_with_gc_background: %08lx", gc->background);
+  pgtk_set_cr_source_with_color(f, gc->background);
+}
+
+void
+pgtk_set_cr_source_with_color (struct frame *f, unsigned long color)
+{
+  PGTK_TRACE("pgtk_set_cr_source_with_color: %08lx.", color);
+  Emacs_Color col;
+  col.pixel = color;
+  pgtk_query_color(f, &col);
+  cairo_set_source_rgb (FRAME_CR_CONTEXT (f), col.red / 65535.0,
+                       col.green / 65535.0, col.blue / 65535.0);
+}
+
+void
+pgtk_cr_draw_frame (cairo_t *cr, struct frame *f)
+{
+  PGTK_TRACE("pgtk_cr_draw_frame");
+  cairo_set_source_surface(cr, FRAME_CR_SURFACE(f), 0, 0);
+  cairo_paint(cr);
+}
+
+void
+pgtk_cr_destroy_surface(struct frame *f)
+{
+  PGTK_TRACE("pgtk_cr_destroy_surface");
+  if (FRAME_CR_CONTEXT(f) != NULL) {
+    cairo_destroy(FRAME_CR_CONTEXT(f));
+    FRAME_CR_CONTEXT(f) = NULL;
+  }
+  if (FRAME_CR_SURFACE(f) != NULL) {
+    cairo_surface_destroy(FRAME_CR_SURFACE(f));
+    FRAME_CR_SURFACE(f) = NULL;
+  }
+  SET_FRAME_GARBAGED (f);
+}
+
+void
+init_pgtkterm (void)
+{
+}
diff --git a/src/pgtkterm.h b/src/pgtkterm.h
new file mode 100644
index 0000000..c01d846
--- /dev/null
+++ b/src/pgtkterm.h
@@ -0,0 +1,569 @@
+/* Definitions and headers for communication with pure Gtk+3.
+   Copyright (C) 1989, 1993, 2005, 2008-2017 Free Software Foundation,
+   Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+
+#include "dispextern.h"
+#include "frame.h"
+#include "character.h"
+#include "font.h"
+#include "sysselect.h"
+
+#ifdef HAVE_PGTK
+
+#include <gtk/gtk.h>
+
+// #define PGTK_DEBUG 1
+
+#ifdef PGTK_DEBUG
+extern void pgtk_log(const char *file, int lineno, const char *fmt, ...)
+  ATTRIBUTE_FORMAT_PRINTF (3, 4);
+#define PGTK_TRACE(fmt, ...) pgtk_log(__FILE__, __LINE__, fmt, ## __VA_ARGS__)
+extern void pgtk_backtrace(const char *file, int lineno);
+#define PGTK_BACKTRACE() pgtk_backtrace(__FILE__, __LINE__)
+#else
+#define PGTK_TRACE(fmt, ...) ((void) 0)
+#define PGTK_BACKTRACE() ((void) 0)
+#endif
+
+/* The GtkTooltip API came in 2.12, but gtk-enable-tooltips in 2.14. */
+#if GTK_CHECK_VERSION (2, 14, 0)
+#define USE_GTK_TOOLTIP
+#endif
+
+/* could use list to store these, but rest of emacs has a big infrastructure
+   for managing a table of bitmap "records" */
+struct pgtk_bitmap_record
+{
+  void *img;
+  char *file;
+  int refcount;
+  int height, width, depth;
+};
+
+#define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b))
+#define ARGB_TO_ULONG(a, r, g, b) (((a) << 24) | ((r) << 16) | ((g) << 8) | 
(b))
+
+#define ALPHA_FROM_ULONG(color) ((color) >> 24)
+#define RED_FROM_ULONG(color) (((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color) (((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color) ((color) & 0xff)
+
+struct scroll_bar
+{
+  /* These fields are shared by all vectors.  */
+  union vectorlike_header header;
+
+  /* The window we're a scroll bar for.  */
+  Lisp_Object window;
+
+  /* The next and previous in the chain of scroll bars in this frame.  */
+  Lisp_Object next, prev;
+
+  /* Fields from `x_window' down will not be traced by the GC.  */
+
+  /* The X window representing this scroll bar.  */
+  Window x_window;
+
+  /* The position and size of the scroll bar in pixels, relative to the
+     frame.  */
+  int top, left, width, height;
+
+  /* The starting and ending positions of the handle, relative to the
+     handle area (i.e. zero is the top position, not
+     SCROLL_BAR_TOP_BORDER).  If they're equal, that means the handle
+     hasn't been drawn yet.
+
+     These are not actually the locations where the beginning and end
+     are drawn; in order to keep handles from becoming invisible when
+     editing large files, we establish a minimum height by always
+     drawing handle bottoms VERTICAL_SCROLL_BAR_MIN_HANDLE pixels below
+     where they would be normally; the bottom and top are in a
+     different co-ordinate system.  */
+  int start, end;
+
+  /* If the scroll bar handle is currently being dragged by the user,
+     this is the number of pixels from the top of the handle to the
+     place where the user grabbed it.  If the handle isn't currently
+     being dragged, this is -1.  */
+  int dragging;
+
+#if defined (USE_TOOLKIT_SCROLL_BARS) && defined (USE_LUCID)
+  /* Last scroll bar part seen in xaw_jump_callback and xaw_scroll_callback.  
*/
+  enum scroll_bar_part last_seen_part;
+#endif
+
+#if defined (USE_TOOLKIT_SCROLL_BARS) && !defined (USE_GTK)
+  /* Last value of whole for horizontal scrollbars.  */
+  int whole;
+#endif
+
+  /* True if the scroll bar is horizontal.  */
+  bool horizontal;
+};
+
+
+/* init'd in pgtk_initialize_display_info () */
+struct pgtk_display_info
+{
+  /* Chain of all pgtk_display_info structures.  */
+  struct pgtk_display_info *next;
+
+  /* The generic display parameters corresponding to this PGTK display. */
+  struct terminal *terminal;
+
+  /* This says how to access this display in Gdk.  */
+  GdkDisplay *gdpy;
+
+  /* This is a cons cell of the form (NAME . FONT-LIST-CACHE).  */
+  Lisp_Object name_list_element;
+
+  /* Number of frames that are on this display.  */
+  int reference_count;
+
+  /* Logical identifier of this display.  */
+  unsigned x_id;
+
+  /* Default name for all frames on this display.  */
+  char *x_id_name;
+
+  /* The number of fonts loaded. */
+  int n_fonts;
+
+  /* Minimum width over all characters in all fonts in font_table.  */
+  int smallest_char_width;
+
+  /* Minimum font height over all fonts in font_table.  */
+  int smallest_font_height;
+
+  struct pgtk_bitmap_record *bitmaps;
+  ptrdiff_t bitmaps_size;
+  ptrdiff_t bitmaps_last;
+
+  /* DPI resolution of this screen */
+  double resx, resy;
+
+  /* Mask of things that cause the mouse to be grabbed */
+  int grabbed;
+
+  int n_planes;
+
+  int color_p;
+
+  Window root_window;
+
+  /* Xism */
+  XrmDatabase xrdb;
+
+  /* The cursor to use for vertical scroll bars. */
+  Emacs_Cursor vertical_scroll_bar_cursor;
+
+  /* The cursor to use for horizontal scroll bars. */
+  Emacs_Cursor horizontal_scroll_bar_cursor;
+
+  /* Information about the range of text currently shown in
+     mouse-face.  */
+  Mouse_HLInfo mouse_highlight;
+
+  struct frame *x_highlight_frame;
+  struct frame *x_focus_frame;
+
+  /* The last frame mentioned in a FocusIn or FocusOut event.  This is
+     separate from x_focus_frame, because whether or not LeaveNotify
+     events cause us to lose focus depends on whether or not we have
+     received a FocusIn event for it.  */
+  struct frame *x_focus_event_frame;
+
+  /* The frame where the mouse was last time we reported a mouse event.  */
+  struct frame *last_mouse_frame;
+
+  /* The frame where the mouse was last time we reported a mouse motion.  */
+  struct frame *last_mouse_motion_frame;
+
+  /* Position where the mouse was last time we reported a motion.
+     This is a position on last_mouse_motion_frame.  */
+  int last_mouse_motion_x;
+  int last_mouse_motion_y;
+
+  /* Where the mouse was last time we reported a mouse position.  */
+  XRectangle last_mouse_glyph;
+
+  /* Time of last mouse movement.  */
+  Time last_mouse_movement_time;
+
+  /* The scroll bar in which the last motion event occurred.  */
+  void *last_mouse_scroll_bar;
+
+  /* The GDK cursor for scroll bars and popup menus.  */
+  GdkCursor *xg_cursor;
+
+
+  /* The frame where the mouse was last time we reported a mouse position.  */
+  struct frame *last_mouse_glyph_frame;
+};
+
+/* This is a chain of structures for all the PGTK displays currently in use.  
*/
+extern struct pgtk_display_info *x_display_list;
+
+struct pgtk_output
+{
+#if 0
+  void *view;
+  void *miniimage;
+#endif
+  unsigned long cursor_color;
+  unsigned long foreground_color;
+  unsigned long background_color;
+  void *toolbar;
+
+  /* Cursors */
+  Emacs_Cursor current_cursor;
+  Emacs_Cursor text_cursor;
+  Emacs_Cursor nontext_cursor;
+  Emacs_Cursor modeline_cursor;
+  Emacs_Cursor hand_cursor;
+  Emacs_Cursor hourglass_cursor;
+  Emacs_Cursor horizontal_drag_cursor;
+  Emacs_Cursor vertical_drag_cursor;
+  Emacs_Cursor left_edge_cursor;
+  Emacs_Cursor top_left_corner_cursor;
+  Emacs_Cursor top_edge_cursor;
+  Emacs_Cursor top_right_corner_cursor;
+  Emacs_Cursor right_edge_cursor;
+  Emacs_Cursor bottom_right_corner_cursor;
+  Emacs_Cursor bottom_edge_cursor;
+  Emacs_Cursor bottom_left_corner_cursor;
+
+  /* PGTK-specific */
+  Emacs_Cursor current_pointer;
+
+  /* Widget whose cursor is hourglass_cursor.  This widget is temporarily
+     mapped to display an hourglass cursor.  */
+  GtkWidget *hourglass_widget;
+
+  XGCValues cursor_xgcv;
+
+  /* lord knows why Emacs needs to know about our Window ids.. */
+  Window window_desc, parent_desc;
+  char explicit_parent;
+
+  struct font *font;
+  int baseline_offset;
+
+  /* If a fontset is specified for this frame instead of font, this
+     value contains an ID of the fontset, else -1.  */
+  int fontset; /* only used with font_backend */
+
+  int icon_top;
+  int icon_left;
+
+  /* The size of the extra width currently allotted for vertical
+     scroll bars, in pixels.  */
+  int vertical_scroll_bar_extra;
+
+  /* The height of the titlebar decoration (included in PGTKWindow's frame). */
+  int titlebar_height;
+
+  /* The height of the toolbar if displayed, else 0. */
+  int toolbar_height;
+
+  /* This is the Emacs structure for the PGTK display this frame is on.  */
+  struct pgtk_display_info *display_info;
+
+  /* Non-zero if we are zooming (maximizing) the frame.  */
+  int zooming;
+
+  /* Non-zero if we are doing an animation, e.g. toggling the tool bar. */
+  int in_animation;
+
+  /* The last size hints set.  */
+  GdkGeometry size_hints;
+  long hint_flags;
+
+  /* The widget of this screen.  This is the window of a top widget.  */
+  GtkWidget *widget;
+  /* The widget of the edit portion of this screen; the window in
+     "window_desc" is inside of this.  */
+  GtkWidget *edit_widget;
+  /* The widget used for laying out widgets vertically.  */
+  GtkWidget *vbox_widget;
+  /* The widget used for laying out widgets horizontally.  */
+  GtkWidget *hbox_widget;
+  /* The menubar in this frame.  */
+  GtkWidget *menubar_widget;
+  /* The tool bar in this frame  */
+  GtkWidget *toolbar_widget;
+  /* True if tool bar is packed into the hbox widget (i.e. vertical).  */
+  bool_bf toolbar_in_hbox : 1;
+  bool_bf toolbar_is_packed : 1;
+
+#ifdef USE_GTK_TOOLTIP
+  GtkTooltip *ttip_widget;
+  GtkWidget *ttip_lbl;
+  GtkWindow *ttip_window;
+#endif /* USE_GTK_TOOLTIP */
+
+  /* Height of menu bar widget, in pixels.  This value
+     is not meaningful if the menubar is turned off.  */
+  int menubar_height;
+
+  /* Height of tool bar widget, in pixels.  top_height is used if tool bar
+     at top, bottom_height if tool bar is at the bottom.
+     Zero if not using an external tool bar or if tool bar is vertical.  */
+  int toolbar_top_height, toolbar_bottom_height;
+
+  /* Width of tool bar widget, in pixels.  left_width is used if tool bar
+     at left, right_width if tool bar is at the right.
+     Zero if not using an external tool bar or if tool bar is horizontal.  */
+  int toolbar_left_width, toolbar_right_width;
+
+#ifdef USE_CAIRO
+  /* Cairo drawing context.  */
+  cairo_t *cr_context;
+  /* Cairo surface for double buffering */
+  cairo_surface_t *cr_surface;
+  cairo_surface_t *cr_surface_visible_bell;
+#endif
+  struct atimer *atimer_visible_bell;
+
+  int has_been_visible;
+
+  /* Relief GCs, colors etc.  */
+  struct relief
+  {
+    XGCValues xgcv;
+    unsigned long pixel;
+  }
+  black_relief, white_relief;
+
+  /* The background for which the above relief GCs were set up.
+     They are changed only when a different background is involved.  */
+  unsigned long relief_background;
+
+  /* Keep track of focus.  May be EXPLICIT if we received a FocusIn for this
+     frame, or IMPLICIT if we received an EnterNotify.
+     FocusOut and LeaveNotify clears EXPLICIT/IMPLICIT. */
+  int focus_state;
+};
+
+/* this dummy decl needed to support TTYs */
+struct x_output
+{
+  int unused;
+};
+
+enum
+{
+  /* Values for focus_state, used as bit mask.
+     EXPLICIT means we received a FocusIn for the frame and know it has
+     the focus.  IMPLICIT means we received an EnterNotify and the frame
+     may have the focus if no window manager is running.
+     FocusOut and LeaveNotify clears EXPLICIT/IMPLICIT. */
+  FOCUS_NONE     = 0,
+  FOCUS_IMPLICIT = 1,
+  FOCUS_EXPLICIT = 2
+};
+
+/* This gives the pgtk_display_info structure for the display F is on.  */
+#define FRAME_X_OUTPUT(f)         ((f)->output_data.pgtk)
+
+#define FRAME_DISPLAY_INFO(f)     (FRAME_X_OUTPUT(f)->display_info)
+#define FRAME_FOREGROUND_COLOR(f) (FRAME_X_OUTPUT(f)->foreground_color)
+#define FRAME_BACKGROUND_COLOR(f) (FRAME_X_OUTPUT(f)->background_color)
+#define FRAME_CURSOR_COLOR(f)     (FRAME_X_OUTPUT(f)->cursor_color)
+#define FRAME_POINTER_TYPE(f)     (FRAME_X_OUTPUT(f)->current_pointer)
+#define FRAME_FONT(f)             (FRAME_X_OUTPUT(f)->font)
+#define FRAME_GTK_OUTER_WIDGET(f) (FRAME_X_OUTPUT(f)->widget)
+#define FRAME_GTK_WIDGET(f)       (FRAME_X_OUTPUT(f)->edit_widget)
+
+/* aliases */
+#define FRAME_PGTK_VIEW(f)         FRAME_GTK_WIDGET(f)
+#define FRAME_X_WINDOW(f)          FRAME_GTK_WIDGET(f)
+
+#define FRAME_X_DISPLAY(f)        (FRAME_DISPLAY_INFO(f)->gdpy)
+
+#define DEFAULT_GDK_DISPLAY() gdk_display_get_default()
+
+/* Turning a lisp vector value into a pointer to a struct scroll_bar.  */
+#define XSCROLL_BAR(vec) ((struct scroll_bar *) XVECTOR (vec))
+
+#define PGTK_FACE_FOREGROUND(f) ((f)->foreground)
+#define PGTK_FACE_BACKGROUND(f) ((f)->background)
+#define FRAME_DEFAULT_FACE(f) FACE_FROM_ID_OR_NULL (f, DEFAULT_FACE_ID)
+
+/* Compute pixel height of the frame's titlebar. */
+#define FRAME_PGTK_TITLEBAR_HEIGHT(f)                                     0
+#if 0
+  (PGTKHeight([FRAME_PGTK_VIEW (f) frame]) == 0 ?                           \
+   0                                                                    \
+   : (int)(PGTKHeight([FRAME_PGTK_VIEW (f) window].frame)                   \
+           - PGTKHeight([PGTKWindow contentRectForFrameRect:                \
+                       [[FRAME_PGTK_VIEW (f) window] frame]               \
+                       styleMask:[[FRAME_PGTK_VIEW (f) window] styleMask]])))
+#endif
+
+/* Compute pixel size for vertical scroll bars */
+#define PGTK_SCROLL_BAR_WIDTH(f)                                       \
+  (FRAME_HAS_VERTICAL_SCROLL_BARS (f)                                  \
+   ? rint (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0                       \
+          ? FRAME_CONFIG_SCROLL_BAR_WIDTH (f)                          \
+          : (FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f)))      \
+   : 0)
+
+/* Compute pixel size for horizontal scroll bars */
+#define PGTK_SCROLL_BAR_HEIGHT(f)                                      \
+  (FRAME_HAS_HORIZONTAL_SCROLL_BARS (f)                                        
\
+   ? rint (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0                      \
+          ? FRAME_CONFIG_SCROLL_BAR_HEIGHT (f)                         \
+          : (FRAME_SCROLL_BAR_LINES (f) * FRAME_LINE_HEIGHT (f)))      \
+   : 0)
+
+/* Difference btwn char-column-calculated and actual SB widths.
+   This is only a concern for rendering when SB on left. */
+#define PGTK_SCROLL_BAR_ADJUST(w, f)                           \
+  (WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_LEFT (w) ?                        \
+   (FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f)         \
+    - PGTK_SCROLL_BAR_WIDTH (f)) : 0)
+
+/* Difference btwn char-line-calculated and actual SB heights.
+   This is only a concern for rendering when SB on top. */
+#define PGTK_SCROLL_BAR_ADJUST_HORIZONTALLY(w, f)              \
+  (WINDOW_HAS_HORIZONTAL_SCROLL_BARS (w) ?             \
+   (FRAME_SCROLL_BAR_LINES (f) * FRAME_LINE_HEIGHT (f) \
+    - PGTK_SCROLL_BAR_HEIGHT (f)) : 0)
+
+#define FRAME_MENUBAR_HEIGHT(f) (FRAME_X_OUTPUT(f)->menubar_height)
+
+/* Calculate system coordinates of the left and top of the parent
+   window or, if there is no parent window, the screen. */
+#define PGTK_PARENT_WINDOW_LEFT_POS(f)                                    \
+  (FRAME_PARENT_FRAME (f) != NULL                                       \
+   ? [[FRAME_PGTK_VIEW (f) window] parentWindow].frame.origin.x : 0)
+#define PGTK_PARENT_WINDOW_TOP_POS(f)                                     \
+  (FRAME_PARENT_FRAME (f) != NULL                                       \
+   ? ([[FRAME_PGTK_VIEW (f) window] parentWindow].frame.origin.y          \
+      + [[FRAME_PGTK_VIEW (f) window] parentWindow].frame.size.height     \
+      - FRAME_PGTK_TITLEBAR_HEIGHT (FRAME_PARENT_FRAME (f)))              \
+   : [[[PGTKScreen screepgtk] objectAtIndex: 0] frame].size.height)
+
+#define FRAME_PGTK_FONT_TABLE(f) (FRAME_DISPLAY_INFO (f)->font_table)
+
+#define FRAME_TOOLBAR_TOP_HEIGHT(f) ((f)->output_data.pgtk->toolbar_top_height)
+#define FRAME_TOOLBAR_BOTTOM_HEIGHT(f) \
+  ((f)->output_data.pgtk->toolbar_bottom_height)
+#define FRAME_TOOLBAR_HEIGHT(f) \
+  (FRAME_TOOLBAR_TOP_HEIGHT (f) + FRAME_TOOLBAR_BOTTOM_HEIGHT (f))
+#define FRAME_TOOLBAR_LEFT_WIDTH(f) ((f)->output_data.pgtk->toolbar_left_width)
+#define FRAME_TOOLBAR_RIGHT_WIDTH(f) 
((f)->output_data.pgtk->toolbar_right_width)
+#define FRAME_TOOLBAR_WIDTH(f) \
+  (FRAME_TOOLBAR_LEFT_WIDTH (f) + FRAME_TOOLBAR_RIGHT_WIDTH (f))
+
+#define FRAME_FONTSET(f) (FRAME_X_OUTPUT(f)->fontset)
+
+#define FRAME_BASELINE_OFFSET(f) (FRAME_X_OUTPUT(f)->baseline_offset)
+#define BLACK_PIX_DEFAULT(f) 0x000000
+#define WHITE_PIX_DEFAULT(f) 0xFFFFFF
+
+/* First position where characters can be shown (instead of scrollbar, if
+   it is on left. */
+#define FIRST_CHAR_POSITION(f)                         \
+  (! (FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT (f)) ? 0  \
+   : FRAME_SCROLL_BAR_COLS (f))
+
+
+/* Display init/shutdown functions implemented in pgtkterm.c */
+extern struct pgtk_display_info *pgtk_term_init (Lisp_Object display_name, 
char *resource_name);
+extern void pgtk_term_shutdown (int sig);
+
+/* Implemented in pgtkterm, published in or needed from pgtkfns. */
+extern void pgtk_clear_frame (struct frame *f);
+extern char *pgtk_xlfd_to_fontname (const char *xlfd);
+
+/* Implemented in pgtkfns. */
+extern void pgtk_set_doc_edited (void);
+extern const char *pgtk_get_defaults_value (const char *key);
+
+/* Color management implemented in pgtkterm. */
+extern bool pgtk_defined_color (struct frame *f,
+                               const char *name,
+                               Emacs_Color *color_def, bool alloc,
+                               bool makeIndex);
+extern void pgtk_query_color (struct frame *f, Emacs_Color *color);
+extern void pgtk_query_colors (struct frame *f, Emacs_Color *colors, int 
ncolors);
+extern int pgtk_parse_color (const char *color_name, Emacs_Color *color);
+extern int pgtk_lisp_to_color (Lisp_Object color, Emacs_Color *col);
+
+/* Implemented in pgtkterm.c */
+extern void pgtk_clear_area (struct frame *f, int x, int y, int width, int 
height);
+extern int pgtk_gtk_to_emacs_modifiers (int state);
+extern void pgtk_clear_under_internal_border (struct frame *f);
+extern void pgtk_set_event_handler(struct frame *f);
+
+/* Implemented in pgtkterm.c */
+extern int x_display_pixel_height (struct pgtk_display_info *);
+extern int x_display_pixel_width (struct pgtk_display_info *);
+
+/* Implemented in pgtkterm.c */
+extern void x_destroy_window (struct frame *f);
+extern void x_set_parent_frame (struct frame *f, Lisp_Object new_value,
+                                Lisp_Object old_value);
+extern void x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value,
+                                   Lisp_Object old_value);
+extern void x_set_no_accept_focus (struct frame *f, Lisp_Object new_value,
+                                   Lisp_Object old_value);
+extern void x_set_z_group (struct frame *f, Lisp_Object new_value,
+                           Lisp_Object old_value);
+extern int pgtk_select (int nfds, fd_set *readfds, fd_set *writefds,
+                       fd_set *exceptfds, struct timespec *timeout,
+                       sigset_t *sigmask);
+
+/* Cairo related functions implemented in pgtkterm.c */
+extern cairo_t *pgtk_begin_cr_clip (struct frame *f);
+extern void pgtk_end_cr_clip (struct frame *f);
+extern void pgtk_set_cr_source_with_gc_foreground (struct frame *f, XGCValues 
*gc);
+extern void pgtk_set_cr_source_with_gc_background (struct frame *f, XGCValues 
*gc);
+extern void pgtk_set_cr_source_with_color (struct frame *f, unsigned long 
color);
+extern void pgtk_cr_draw_frame (cairo_t *cr, struct frame *f);
+extern void pgtk_cr_destroy_surface(struct frame *f);
+
+/* Symbol initializations implemented in each pgtk sources. */
+extern void syms_of_pgtkterm (void);
+extern void syms_of_pgtkfns (void);
+extern void syms_of_pgtkmenu (void);
+extern void syms_of_pgtkselect (void);
+
+/* Implemented in pgtkselect. */
+extern void nxatoms_of_pgtkselect (void);
+
+/* Initialization and marking implemented in pgtkterm.c */
+extern void init_pgtkterm (void);
+extern void mark_pgtkterm(void);
+extern void pgtk_delete_terminal (struct terminal *terminal);
+
+extern void pgtk_make_frame_visible (struct frame *f);
+extern void pgtk_make_frame_invisible (struct frame *f);
+extern void x_wm_set_size_hint (struct frame *, long, bool);
+extern void x_free_frame_resources (struct frame *);
+extern void pgtk_iconify_frame (struct frame *f);
+extern void x_focus_frame (struct frame *f, bool noactivate);
+extern void pgtk_set_scroll_bar_default_width (struct frame *f);
+extern void pgtk_set_scroll_bar_default_height (struct frame *f);
+extern Lisp_Object x_get_focus_frame (struct frame *frame);
+
+
+#endif /* HAVE_PGTK */
diff --git a/src/process.c b/src/process.c
index bf64ead..9926993 100644
--- a/src/process.c
+++ b/src/process.c
@@ -5599,8 +5599,12 @@ wait_reading_process_output (intmax_t time_limit, int 
nsecs, int read_kbd,
            timeout = make_timespec (0, 0);
 #endif
 
+#if defined HAVE_PGTK
+         nfds = pgtk_select (max_desc + 1,
+                             &Available, (check_write ? &Writeok : 0),
+                             NULL, &timeout, NULL);
+#elif defined HAVE_GLIB && !defined HAVE_NS
          /* Non-macOS HAVE_GLIB builds call thread_select in xgselect.c.  */
-#if defined HAVE_GLIB && !defined HAVE_NS
          nfds = xg_select (max_desc + 1,
                            &Available, (check_write ? &Writeok : 0),
                            NULL, &timeout, NULL);
diff --git a/src/termhooks.h b/src/termhooks.h
index 44ab142..4403d51 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -60,7 +60,8 @@ enum output_method
   output_x_window,
   output_msdos_raw,
   output_w32,
-  output_ns
+  output_ns,
+  output_pgtk
 };
 
 /* Input queue declarations and hooks.  */
@@ -445,6 +446,7 @@ struct terminal
     struct x_display_info *x;         /* xterm.h */
     struct w32_display_info *w32;     /* w32term.h */
     struct ns_display_info *ns;       /* nsterm.h */
+    struct pgtk_display_info *pgtk; /* pgtkterm.h */
   } display_info;
 
 
@@ -518,7 +520,7 @@ struct terminal
      BGCOLOR.  */
   void (*query_frame_background_color) (struct frame *f, Emacs_Color *bgcolor);
 
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI) || defined (HAVE_PGTK)
   /* On frame F, translate pixel colors to RGB values for the NCOLORS
      colors in COLORS.  Use cached information, if available.  */
 
@@ -833,6 +835,9 @@ extern struct terminal *terminal_list;
 #elif defined (HAVE_NS)
 #define TERMINAL_FONT_CACHE(t)                                         \
   (t->type == output_ns ? t->display_info.ns->name_list_element : Qnil)
+#elif defined (HAVE_PGTK)
+#define TERMINAL_FONT_CACHE(t)                                         \
+  (t->type == output_pgtk ? t->display_info.pgtk->name_list_element : Qnil)
 #endif
 
 extern struct terminal *decode_live_terminal (Lisp_Object);
diff --git a/src/terminal.c b/src/terminal.c
index e3b666b..ef2f0c3 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -445,6 +445,8 @@ possible return values.  */)
       return Qpc;
     case output_ns:
       return Qns;
+    case output_pgtk:
+      return Qpgtk;
     default:
       emacs_abort ();
     }
diff --git a/src/xdisp.c b/src/xdisp.c
index e49cc43..e722a75 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -14330,8 +14330,10 @@ redisplay_tool_bar (struct frame *f)
   f->tool_bar_redisplayed = true;
 #ifdef HAVE_EXT_TOOL_BAR
 
+#if 0
   if (FRAME_EXTERNAL_TOOL_BAR (f))
     update_frame_tool_bar (f);
+#endif
   return false;
 
 #else /* ! (HAVE_EXT_TOOL_BAR) */
@@ -15485,7 +15487,7 @@ redisplay_internal (void)
   if (!fr->glyphs_initialized_p)
     return;
 
-#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS)
+#if defined (USE_X_TOOLKIT) || (defined (USE_GTK) && !defined(HAVE_PGTK)) || 
defined (HAVE_NS)
   if (popup_activated ())
     {
       return;
@@ -28970,6 +28972,16 @@ draw_glyphs (struct window *w, int x, struct glyph_row 
*row,
   return x_reached;
 }
 
+static int draw_glyphs_debug(const char *file, int lineno,
+                            struct window *w, int x, struct glyph_row *row,
+                            enum glyph_row_area area, ptrdiff_t start, 
ptrdiff_t end,
+                            enum draw_glyphs_face hl, int overlaps)
+{
+  return draw_glyphs(w, x, row, area, start, end, hl, overlaps);
+}
+#define draw_glyphs(w, x, r, a, s, e, h, o) \
+  draw_glyphs_debug(__FILE__, __LINE__, w, x, r, a, s, e, h, o)
+
 /* Find the first glyph in the run of underlined glyphs preceding the
    beginning of glyph string S, and return its font (which could be
    NULL).  This is needed because that font determines the underline
@@ -32550,7 +32562,7 @@ mouse_face_from_buffer_pos (Lisp_Object window,
   hlinfo->mouse_face_face_id
     = face_at_buffer_position (w, mouse_charpos, &ignore,
                               mouse_charpos + 1,
-                               !hlinfo->mouse_face_hidden, -1, 0);
+                              !hlinfo->mouse_face_hidden, -1, 0);
   show_mouse_face (hlinfo, DRAW_MOUSE_FACE);
 }
 
@@ -33281,7 +33293,7 @@ note_mouse_highlight (struct frame *f, int x, int y)
   struct buffer *b;
 
   /* When a menu is active, don't highlight because this looks odd.  */
-#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) || 
defined (MSDOS)
+#if defined (USE_X_TOOLKIT) || (defined (USE_GTK) && !defined(HAVE_PGTK)) || 
defined (HAVE_NS) || defined (MSDOS)
   if (popup_activated ())
     return;
 #endif
diff --git a/src/xfaces.c b/src/xfaces.c
index 73a536b..a84e521 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -245,6 +245,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #ifdef HAVE_NS
 #define GCGraphicsExposures 0
 #endif /* HAVE_NS */
+
+#ifdef HAVE_PGTK
+#define GCGraphicsExposures 0
+#endif /* HAVE_NS */
 #endif /* HAVE_WINDOW_SYSTEM */
 
 #include "buffer.h"
@@ -571,6 +575,26 @@ x_free_gc (struct frame *f, Emacs_GC *gc)
 }
 #endif  /* HAVE_NS */
 
+#ifdef HAVE_PGTK
+/* PGTK emulation of GCs */
+
+static GC
+x_create_gc (struct frame *f,
+            unsigned long mask,
+            XGCValues *xgcv)
+{
+  GC gc = xmalloc (sizeof *gc);
+  *gc = *xgcv;
+  return gc;
+}
+
+static void
+x_free_gc (struct frame *f, GC gc)
+{
+  xfree (gc);
+}
+#endif  /* HAVE_NS */
+
 /***********************************************************************
                           Frames and faces
  ***********************************************************************/
diff --git a/src/xsettings.c b/src/xsettings.c
index 1ba1021..444bea0 100644
--- a/src/xsettings.c
+++ b/src/xsettings.c
@@ -26,7 +26,11 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <byteswap.h>
 
 #include "lisp.h"
+#ifndef HAVE_PGTK
 #include "xterm.h"
+#else
+#include "gtkutil.h"
+#endif
 #include "xsettings.h"
 #include "frame.h"
 #include "keyboard.h"
@@ -34,7 +38,12 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "termhooks.h"
 #include "pdumper.h"
 
+#ifndef HAVE_PGTK
 #include <X11/Xproto.h>
+#else
+typedef unsigned short CARD16;
+typedef unsigned int CARD32;
+#endif
 
 #ifdef HAVE_GSETTINGS
 #include <glib-object.h>
@@ -55,7 +64,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 static char *current_mono_font;
 static char *current_font;
-static struct x_display_info *first_dpyinfo;
+static Display_Info *first_dpyinfo;
 static Lisp_Object current_tool_bar_style;
 
 /* Store a config changed event in to the event queue.  */
@@ -73,14 +82,18 @@ store_config_changed_event (Lisp_Object arg, Lisp_Object 
display_name)
 
 /* Return true if DPYINFO is still valid.  */
 static bool
-dpyinfo_valid (struct x_display_info *dpyinfo)
+dpyinfo_valid (Display_Info *dpyinfo)
 {
   bool found = false;
   if (dpyinfo != NULL)
     {
-      struct x_display_info *d;
+      Display_Info *d;
       for (d = x_display_list; !found && d; d = d->next)
+#ifndef HAVE_PGTK
         found = d == dpyinfo && d->display == dpyinfo->display;
+#else
+        found = d == dpyinfo && d->gdpy == dpyinfo->gdpy;
+#endif
     }
   return found;
 }
@@ -149,7 +162,7 @@ map_tool_bar_style (const char *tool_bar_style)
 
 static void
 store_tool_bar_style_changed (const char *newstyle,
-                              struct x_display_info *dpyinfo)
+                              Display_Info *dpyinfo)
 {
   Lisp_Object style = map_tool_bar_style (newstyle);
   if (EQ (current_tool_bar_style, style))
@@ -161,10 +174,12 @@ store_tool_bar_style_changed (const char *newstyle,
                                 XCAR (dpyinfo->name_list_element));
 }
 
+#ifndef HAVE_PGTK
 #if defined USE_CAIRO || defined HAVE_XFT
 #define XSETTINGS_FONT_NAME       "Gtk/FontName"
 #endif
 #define XSETTINGS_TOOL_BAR_STYLE  "Gtk/ToolbarStyle"
+#endif
 
 enum {
   SEEN_AA         = 0x01,
@@ -321,10 +336,11 @@ something_changed_gconfCB (GConfClient *client,
 
 #endif /* USE_CAIRO || HAVE_XFT */
 
+#ifndef HAVE_PGTK
 /* Find the window that contains the XSETTINGS property values.  */
 
 static void
-get_prop_window (struct x_display_info *dpyinfo)
+get_prop_window (Display_Info *dpyinfo)
 {
   Display *dpy = dpyinfo->display;
 
@@ -339,6 +355,9 @@ get_prop_window (struct x_display_info *dpyinfo)
 
   XUngrabServer (dpy);
 }
+#endif
+
+#ifndef HAVE_PGTK
 
 #define PAD(nr)    (((nr) + 3) & ~3)
 
@@ -566,13 +585,15 @@ parse_settings (unsigned char *prop,
 
   return settings_seen;
 }
+#endif
 
+#ifndef HAVE_PGTK
 /* Read settings from the XSettings property window on display for DPYINFO.
    Store settings read in SETTINGS.
    Return true iff successful.  */
 
 static bool
-read_settings (struct x_display_info *dpyinfo, struct xsettings *settings)
+read_settings (Display_Info *dpyinfo, struct xsettings *settings)
 {
   Atom act_type;
   int act_form;
@@ -600,12 +621,14 @@ read_settings (struct x_display_info *dpyinfo, struct 
xsettings *settings)
 
   return got_settings;
 }
+#endif
 
+#ifndef HAVE_PGTK
 /* Apply Xft settings in SETTINGS to the Xft library.
    Store a Lisp event that Xft settings changed.  */
 
 static void
-apply_xft_settings (struct x_display_info *dpyinfo,
+apply_xft_settings (Display_Info *dpyinfo,
                     struct xsettings *settings)
 {
 #ifdef HAVE_XFT
@@ -731,12 +754,14 @@ apply_xft_settings (struct x_display_info *dpyinfo,
     FcPatternDestroy (pat);
 #endif /* HAVE_XFT */
 }
+#endif
 
+#ifndef HAVE_PGTK
 /* Read XSettings from the display for DPYINFO.
    If SEND_EVENT_P store a Lisp event settings that changed.  */
 
 static void
-read_and_apply_settings (struct x_display_info *dpyinfo, bool send_event_p)
+read_and_apply_settings (Display_Info *dpyinfo, bool send_event_p)
 {
   struct xsettings settings;
 
@@ -763,11 +788,13 @@ read_and_apply_settings (struct x_display_info *dpyinfo, 
bool send_event_p)
     }
 #endif
 }
+#endif
 
+#ifndef HAVE_PGTK
 /* Check if EVENT for the display in DPYINFO is XSettings related.  */
 
 void
-xft_settings_event (struct x_display_info *dpyinfo, const XEvent *event)
+xft_settings_event (Display_Info *dpyinfo, const XEvent *event)
 {
   bool check_window_p = false, apply_settings_p = false;
 
@@ -805,6 +832,7 @@ xft_settings_event (struct x_display_info *dpyinfo, const 
XEvent *event)
   if (apply_settings_p)
     read_and_apply_settings (dpyinfo, true);
 }
+#endif
 
 /* Initialize GSettings and read startup values.  */
 
@@ -940,10 +968,11 @@ init_gconf (void)
 #endif /* HAVE_GCONF */
 }
 
+#ifndef HAVE_PGTK
 /* Init Xsettings and read startup values.  */
 
 static void
-init_xsettings (struct x_display_info *dpyinfo)
+init_xsettings (Display_Info *dpyinfo)
 {
   Display *dpy = dpyinfo->display;
 
@@ -959,13 +988,16 @@ init_xsettings (struct x_display_info *dpyinfo)
 
   unblock_input ();
 }
+#endif
 
 void
-xsettings_initialize (struct x_display_info *dpyinfo)
+xsettings_initialize (Display_Info *dpyinfo)
 {
   if (first_dpyinfo == NULL) first_dpyinfo = dpyinfo;
   init_gconf ();
+#ifndef HAVE_PGTK
   init_xsettings (dpyinfo);
+#endif
   init_gsettings ();
 }
 
diff --git a/src/xsettings.h b/src/xsettings.h
index f29ce77..1d2f1c6 100644
--- a/src/xsettings.h
+++ b/src/xsettings.h
@@ -20,12 +20,23 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #ifndef XSETTINGS_H
 #define XSETTINGS_H
 
+#ifndef HAVE_PGTK
 #include <X11/Xlib.h>
+#endif
 
 struct x_display_info;
+struct pgtk_display_info;
+
+#ifndef HAVE_PGTK
+typedef struct x_display_info Display_Info;
+#else
+typedef struct pgtk_display_info Display_Info;
+#endif
 
-extern void xsettings_initialize (struct x_display_info *);
-extern void xft_settings_event (struct x_display_info *, const XEvent *);
+extern void xsettings_initialize (Display_Info *);
+#ifndef HAVE_PGTK
+extern void xft_settings_event (Display_Info *, const XEvent *);
+#endif
 extern const char *xsettings_get_system_font (void);
 #ifdef USE_LUCID
 extern const char *xsettings_get_system_normal_font (void);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]