]> git.eshelyaron.com Git - emacs.git/commitdiff
Merge branch 'master' of git.sv.gnu.org:/srv/git/emacs into feature/pgtk
authorYuuki Harano <masm+github@masm11.me>
Tue, 27 Apr 2021 12:12:21 +0000 (21:12 +0900)
committerYuuki Harano <masm+github@masm11.me>
Tue, 27 Apr 2021 12:12:21 +0000 (21:12 +0900)
22 files changed:
1  2 
.gitignore
Makefile.in
configure.ac
lisp/faces.el
lisp/frame.el
lisp/loadup.el
lisp/startup.el
src/Makefile.in
src/alloc.c
src/dispextern.h
src/dispnew.c
src/emacs.c
src/frame.c
src/frame.h
src/gtkutil.c
src/image.c
src/keyboard.c
src/pgtkfns.c
src/pgtkterm.c
src/process.c
src/termhooks.h
src/xdisp.c

diff --cc .gitignore
Simple merge
diff --cc Makefile.in
index 64ad53abbf9b02c88e23435145d5ae587b4c6799,8d52cb5857511e714bb9a54fed24f730009db21e..3115e63025f5087ee82245e41d04bf6082e7d417
@@@ -320,9 -321,15 +326,17 @@@ CONFIG_STATUS_FILES_IN = 
  COPYDIR = ${srcdir}/etc ${srcdir}/lisp
  COPYDESTS = "$(DESTDIR)${etcdir}" "$(DESTDIR)${lispdir}"
  
 -all: ${SUBDIR} info
+ ifeq (${ns_self_contained},no)
+ BIN_DESTDIR='$(DESTDIR)${bindir}/'
+ ELN_DESTDIR = $(DESTDIR)${libdir}/emacs/${version}/
+ else
+ BIN_DESTDIR='${ns_appbindir}/'
+ ELN_DESTDIR = ${ns_appresdir}/
+ endif
 +gsettings_SCHEMAS = etc/org.gnu.emacs.defaults.gschema.xml
 +
 +all: ${SUBDIR} info $(gsettings_SCHEMAS:.xml=.valid)
  
  .PHONY: all ${SUBDIR} blessmail epaths-force epaths-force-w32 etc-emacsver
  
@@@ -460,7 -470,7 +477,7 @@@ $(srcdir)/configure: $(srcdir)/configur
  ## don't have to duplicate the list of utilities to install in
  ## this Makefile as well.
  
- install: all install-arch-indep install-etcdoc install-arch-dep install-$(NTDIR) blessmail install-gsettings-schemas
 -install: all install-arch-indep install-etcdoc install-arch-dep install-$(NTDIR) blessmail install-eln
++install: all install-arch-indep install-etcdoc install-arch-dep install-$(NTDIR) blessmail install-eln install-gsettings-schemas
        @true
  
  ## Ensure that $subdir contains a subdirs.el file.
diff --cc configure.ac
index 0b5c0fe72f1068aeb31a24ec5070dab72860450d,3df4359fa72d716e9e9d6bc06a4b9d0fee5eb820..61b747b4cd7741145da377cee1e2677d4e0b72af
@@@ -5780,8 -5825,8 +5904,8 @@@ optsep
  emacs_config_features=
  for opt in ACL CAIRO DBUS FREETYPE GCONF GIF GLIB GMP GNUTLS GPM GSETTINGS \
   HARFBUZZ IMAGEMAGICK JPEG JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 \
-  M17N_FLT MODULES NOTIFY NS OLDXMENU PDUMPER PGTK PNG RSVG SECCOMP SOUND \
-  THREADS TIFF \
 - M17N_FLT MODULES NATIVE_COMP NOTIFY NS OLDXMENU PDUMPER PNG RSVG SECCOMP \
++ M17N_FLT MODULES NATIVE_COMP NOTIFY NS OLDXMENU PDUMPER PGTK PNG RSVG SECCOMP \
+  SOUND THREADS TIFF \
   TOOLKIT_SCROLL_BARS UNEXEC X11 XAW3D XDBE XFT XIM XPM XWIDGETS X_TOOLKIT \
   ZLIB; do
  
diff --cc lisp/faces.el
Simple merge
diff --cc lisp/frame.el
Simple merge
diff --cc lisp/loadup.el
Simple merge
diff --cc lisp/startup.el
Simple merge
diff --cc src/Makefile.in
Simple merge
diff --cc src/alloc.c
Simple merge
Simple merge
diff --cc src/dispnew.c
Simple merge
diff --cc src/emacs.c
Simple merge
diff --cc src/frame.c
Simple merge
diff --cc src/frame.h
Simple merge
diff --cc src/gtkutil.c
index 16487b036d493143c9005927acaff31221ca1696,4ad172bb486612a16be1f64b5a48fdbce1f71597..897eade85032f25e0ecab3a517af4becd8fd7b15
@@@ -1057,23 -954,8 +1042,20 @@@ xg_frame_set_char_size (struct frame *f
    bool was_visible = false;
    bool hide_child_frame;
  
-   if (FRAME_PIXEL_HEIGHT (f) == 0)
-     return;
 +#ifndef HAVE_PGTK
    gtk_window_get_size (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
                       &gwidth, &gheight);
 +#else
 +  if (FRAME_GTK_OUTER_WIDGET (f)) {
 +    gtk_window_get_size (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
 +                       &gwidth, &gheight);
 +  } else {
 +    GtkAllocation alloc;
 +    gtk_widget_get_allocation (FRAME_GTK_WIDGET (f), &alloc);
 +    gwidth = alloc.width;
 +    gheight = alloc.height;
 +  }
 +#endif
  
    /* Do this before resize, as we don't know yet if we will be resized.  */
    FRAME_RIF (f)->clear_under_internal_border (f);
       manager will abolish it.  At least the respective size should
       remain unchanged but giving the frame back its normal size will
       be broken ... */
-   if (EQ (fullscreen, Qfullwidth) && width == FRAME_TEXT_WIDTH (f))
-     {
-       frame_size_history_add
-       (f, Qxg_frame_set_char_size_1, width, height,
-        list2i (gheight, totalheight));
+   if (EQ (fullscreen, Qfullwidth) && width == FRAME_PIXEL_WIDTH (f))
 +#ifndef HAVE_PGTK
-       gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                        gwidth, totalheight);
+     gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                      gwidth, outer_height);
 +#else
-       if (FRAME_GTK_OUTER_WIDGET (f))
-       {
-         gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                            gwidth, totalheight);
-       }
-       else
-       {
-         gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
-                                      gwidth, totalheight);
-       }
++    if (FRAME_GTK_OUTER_WIDGET (f))
++      {
++      gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
++                         gwidth, outer_height);
++      }
++    else
++      {
++      gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
++                                   gwidth, outer_height);
++      }
 +#endif
-     }
-   else if (EQ (fullscreen, Qfullheight) && height == FRAME_TEXT_HEIGHT (f))
-     {
-       frame_size_history_add
-       (f, Qxg_frame_set_char_size_2, width, height,
-        list2i (gwidth, totalwidth));
+   else if (EQ (fullscreen, Qfullheight) && height == FRAME_PIXEL_HEIGHT (f))
 +#ifndef HAVE_PGTK
-       gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                        totalwidth, gheight);
+     gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+                      outer_width, gheight);
 +#else
-       if (FRAME_GTK_OUTER_WIDGET (f))
-       {
-         gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                            totalwidth, gheight);
-       }
-       else
-       {
-         gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
-                                      totalwidth, gheight);
-       }
++    if (FRAME_GTK_OUTER_WIDGET (f))
++      {
++      gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
++                         outer_width, gheight);
++      }
++    else
++      {
++      gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
++                                   outer_width, gheight);
++      }
 +#endif
-     }
    else if (FRAME_PARENT_FRAME (f) && FRAME_VISIBLE_P (f))
      {
        was_visible = true;
                unblock_input ();
              }
  
 +#ifndef HAVE_PGTK
          gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                            totalwidth, totalheight);
+                            outer_width, outer_height);
 +#else
 +        if (FRAME_GTK_OUTER_WIDGET (f)) {
 +          gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                              totalwidth, totalheight);
++                             outer_width, outer_height);
 +        } else {
 +          gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
-                                        totalwidth, totalheight);
++                                       outer_width, outer_height);
 +        }
 +#endif
  
            if (hide_child_frame)
              {
      }
    else
      {
-       frame_size_history_add
-       (f, Qxg_frame_set_char_size_3, width, height,
-        list2i (totalwidth, totalheight));
 +#ifndef HAVE_PGTK
        gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                        totalwidth, totalheight);
+                        outer_width, outer_height);
 +#else
 +      if (FRAME_GTK_OUTER_WIDGET (f)) {
 +      gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                          totalwidth, totalheight);
++                         outer_width, outer_height);
 +      } else {
 +      gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
-                                    totalwidth, totalheight);
++                                   outer_width, outer_height);
 +      }
 +#endif
        fullscreen = Qnil;
      }
  
        /* Must call this to flush out events */
        (void)gtk_events_pending ();
        gdk_flush ();
 +#ifndef HAVE_PGTK
        x_wait_for_event (f, ConfigureNotify);
 +#endif
  
+       if (CONSP (frame_size_history))
+       frame_size_history_extra
+         (f, build_string ("xg_frame_set_char_size, visible"),
+          FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f), width, height,
+          f->new_width, f->new_height);
        if (!NILP (fullscreen))
        /* Try to restore fullscreen state.  */
        {
diff --cc src/image.c
Simple merge
diff --cc src/keyboard.c
Simple merge
diff --cc src/pgtkfns.c
index e9ee2ba13f3bce04a50f227aa11ccab5eec644d7,0000000000000000000000000000000000000000..65c81f7438e388c7e9466b046998d4300f0c2473
mode 100644,000000..100644
--- /dev/null
@@@ -1,4167 -1,0 +1,4140 @@@
-   Lisp_Object fullscreen;
 +/* Functions for the pure Gtk+-3.
 +
 +Copyright (C) 1989, 1992-1994, 2005-2006, 2008-2020 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 int x_decode_color (struct frame *f, Lisp_Object color_name,
 +                         int mono_color);
 +static struct pgtk_display_info *pgtk_display_info_for_name (Lisp_Object);
 +
 +static const char *pgtk_app_name = "Emacs";
 +
 +/* scale factor manually set per monitor */
 +static Lisp_Object monitor_scale_factor_alist;
 +
 +/* ==========================================================================
 +
 +    Internal utility functions
 +
 +   ========================================================================== */
 +
 +static double
 +pgtk_get_monitor_scale_factor (const char *model)
 +{
 +  if (model == NULL)
 +    return 0.0;
 +
 +  Lisp_Object mdl = build_string (model);
 +  Lisp_Object tem = Fassoc(mdl, monitor_scale_factor_alist, Qnil);
 +  if (NILP (tem))
 +    return 0;
 +  Lisp_Object cdr = Fcdr (tem);
 +  if (NILP (cdr))
 +    return 0;
 +  if (FIXNUMP (cdr))
 +    {
 +      return XFIXNUM (cdr);
 +    }
 +  else if (FLOATP (cdr))
 +    {
 +      return XFLOAT_DATA (cdr);
 +    }
 +  else
 +    error ("unknown type of scale-factor");
 +}
 +
 +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)
 +{
 +  unsigned long fg;
 +
 +  fg = x_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
 +  FRAME_FOREGROUND_PIXEL (f) = fg;
 +  FRAME_X_OUTPUT (f)->foreground_color = fg;
 +
 +  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);
 +    }
 +}
 +
 +
 +static void
 +x_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 +{
 +  unsigned long bg;
 +
 +  bg = x_decode_color (f, arg, WHITE_PIX_DEFAULT (f));
 +  FRAME_BACKGROUND_PIXEL (f) = bg;
 +
 +  /* clear the frame */
 +  if (FRAME_VISIBLE_P (f))
 +    pgtk_clear_frame (f);
 +
 +  PGTK_TRACE ("x_set_background_color: col.pixel=%08lx.", bg);
 +  FRAME_X_OUTPUT (f)->background_color = bg;
 +
 +  xg_set_background_color (f, bg);
 +  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);
 +}
 +
 +static void
 +x_set_border_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 +{
 +  int pix;
 +
 +  CHECK_STRING (arg);
 +  pix = x_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
 +  FRAME_X_OUTPUT (f)->border_pixel = pix;
 +  pgtk_frame_rehighlight (FRAME_DISPLAY_INFO (f));
 +}
 +
 +static void
 +x_set_cursor_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 +{
 +  unsigned long fore_pixel, pixel;
 +  struct pgtk_output *x = f->output_data.pgtk;
 +
 +  if (!NILP (Vx_cursor_fore_pixel))
 +    {
 +      fore_pixel = x_decode_color (f, Vx_cursor_fore_pixel,
 +                                 WHITE_PIX_DEFAULT (f));
 +    }
 +  else
 +    fore_pixel = FRAME_BACKGROUND_PIXEL (f);
 +
 +  pixel = x_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
 +
 +  /* Make sure that the cursor color differs from the background color.  */
 +  if (pixel == FRAME_BACKGROUND_PIXEL (f))
 +    {
 +      pixel = x->mouse_color;
 +      if (pixel == fore_pixel)
 +      {
 +        fore_pixel = FRAME_BACKGROUND_PIXEL (f);
 +      }
 +    }
 +
 +  x->cursor_foreground_color = fore_pixel;
 +  x->cursor_color = pixel;
 +
 +  if (FRAME_X_WINDOW (f) != 0)
 +    {
 +      x->cursor_xgcv.background = x->cursor_color;
 +      x->cursor_xgcv.foreground = fore_pixel;
 +
 +      if (FRAME_VISIBLE_P (f))
 +      {
 +        gui_update_cursor (f, false);
 +        gui_update_cursor (f, true);
 +      }
 +    }
 +
 +  update_face_from_frame_parameter (f, Qcursor_color, arg);
 +}
 +
 +static void
 +pgtk_set_name_internal (struct frame *f, Lisp_Object name)
 +{
 +  if (FRAME_GTK_OUTER_WIDGET (f))
 +    {
 +      block_input ();
 +      {
 +      Lisp_Object encoded_name;
 +
 +      /* As ENCODE_UTF_8 may cause GC and relocation of string data,
 +         we use it before x_encode_text that may return string data.  */
 +      encoded_name = ENCODE_UTF_8 (name);
 +
 +      gtk_window_set_title (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
 +                            SSDATA (encoded_name));
 +      }
 +      unblock_input ();
 +    }
 +}
 +
 +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, true);
 +}
 +
 +
 +/* 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
 +pgtk_implicitly_set_name (struct frame *f, Lisp_Object arg,
 +                        Lisp_Object oldval)
 +{
 +  PGTK_TRACE ("x_implicitly_set_name");
 +  pgtk_set_name (f, arg, false);
 +}
 +
 +
 +/* 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);
 +}
 +
 +
 +void
 +pgtk_set_doc_edited (void)
 +{
 +}
 +
 +
 +static void
 +x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
 +{
 +  int nlines;
 +  /* Right now, menu bars don't work properly in minibuf-only frames;
 +     most of the commands try to apply themselves to the minibuffer
 +     frame itself, and get an error because you can't switch buffers
 +     in or split the minibuffer window.  */
 +  if (FRAME_MINIBUF_ONLY_P (f) || FRAME_PARENT_FRAME (f))
 +    return;
 +
 +  if (TYPE_RANGED_FIXNUMP (int, value))
 +      nlines = XFIXNUM (value);
 +  else
 +    nlines = 0;
 +
 +  /* Make sure we redisplay all windows in this frame.  */
 +  fset_redisplay (f);
 +
 +  FRAME_MENU_BAR_LINES (f) = 0;
 +  FRAME_MENU_BAR_HEIGHT (f) = 0;
 +  if (nlines)
 +    {
 +      FRAME_EXTERNAL_MENU_BAR (f) = 1;
 +      if (FRAME_PGTK_P (f) && f->output_data.pgtk->menubar_widget == 0)
 +      /* Make sure next redisplay shows the menu bar.  */
 +      XWINDOW (FRAME_SELECTED_WINDOW (f))->update_mode_line = true;
 +    }
 +  else
 +    {
 +      if (FRAME_EXTERNAL_MENU_BAR (f) == 1)
 +      free_frame_menubar (f);
 +      FRAME_EXTERNAL_MENU_BAR (f) = 0;
 +      if (FRAME_X_P (f))
 +      f->output_data.pgtk->menubar_widget = 0;
 +    }
 +
 +  adjust_frame_glyphs (f);
 +}
 +
 +/* Set the number of lines used for the tab bar of frame F to VALUE.
 +   VALUE not an integer, or < 0 means set the lines to zero.  OLDVAL
 +   is the old number of tab bar lines.  This function changes the
 +   height of all windows on frame F to match the new tab bar height.
 +   The frame's height doesn't change.  */
 +
 +static void
 +x_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
 +{
 +  int nlines;
 +
 +  /* Treat tab bars like menu bars.  */
 +  if (FRAME_MINIBUF_ONLY_P (f))
 +    return;
 +
 +  /* Use VALUE only if an int >= 0.  */
 +  if (RANGED_FIXNUMP (0, value, INT_MAX))
 +    nlines = XFIXNAT (value);
 +  else
 +    nlines = 0;
 +
 +  x_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
 +}
 +
 +
 +/* Set the pixel height of the tab bar of frame F to HEIGHT.  */
 +void
 +x_change_tab_bar_height (struct frame *f, int height)
 +{
 +  int unit = FRAME_LINE_HEIGHT (f);
 +  int old_height = FRAME_TAB_BAR_HEIGHT (f);
 +  int lines = (height + unit - 1) / unit;
-   /* Store the `tab-bar-lines' and `height' frame parameters.  */
++  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
 +
 +  /* Make sure we redisplay all windows in this frame.  */
 +  fset_redisplay (f);
 +
 +  /* Recalculate tab bar and frame text sizes.  */
 +  FRAME_TAB_BAR_HEIGHT (f) = height;
 +  FRAME_TAB_BAR_LINES (f) = lines;
-   store_frame_param (f, Qheight, make_fixnum (FRAME_LINES (f)));
-   /* We also have to make sure that the internal border at the top of
-      the frame, below the menu bar or tab bar, is redrawn when the
-      tab bar disappears.  This is so because the internal border is
-      below the tab bar if one is displayed, but is below the menu bar
-      if there isn't a tab bar.  The tab bar draws into the area
-      below the menu bar.  */
 +  store_frame_param (f, Qtab_bar_lines, make_fixnum (lines));
-   /* Recalculate tabbar height.  */
-   f->n_tab_bar_rows = 0;
-   if (old_height == 0
-       && (!f->after_make_frame
-         || NILP (frame_inhibit_implied_resize)
-         || (CONSP (frame_inhibit_implied_resize)
-             &&
-             NILP (Fmemq (Qtab_bar_lines, frame_inhibit_implied_resize)))))
-     f->tab_bar_redisplayed = f->tab_bar_resized = false;
-   adjust_frame_size (f, -1, -1,
-                    ((!f->tab_bar_resized
-                      && (NILP (fullscreen =
-                                get_frame_param (f, Qfullscreen))
-                          || EQ (fullscreen, Qfullwidth))) ? 1
-                     : (old_height == 0 || height == 0) ? 2
-                     : 4), false, Qtab_bar_lines);
-   f->tab_bar_resized = f->tab_bar_redisplayed;
++
 +  if (FRAME_X_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0)
 +    {
 +      clear_frame (f);
 +      clear_current_matrices (f);
 +    }
 +
 +  if ((height < old_height) && WINDOWP (f->tab_bar_window))
 +    clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
 +
-   int x_width = 0, x_height = 0;
++  if (!f->tab_bar_resized)
++    {
++      /* As long as tab_bar_resized is false, effectively try to change
++        F's native height.  */
++      if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
++      adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
++                         1, false, Qtab_bar_lines);
++      else
++      adjust_frame_size (f, -1, -1, 4, false, Qtab_bar_lines);
++
++      f->tab_bar_resized = f->tab_bar_redisplayed;
++    }
++  else
++    /* Any other change may leave the native size of F alone.  */
++    adjust_frame_size (f, -1, -1, 3, false, Qtab_bar_lines);
 +
 +  /* adjust_frame_size might not have done anything, garbage frame
 +     here.  */
 +  adjust_frame_glyphs (f);
 +  SET_FRAME_GARBAGED (f);
 +  if (FRAME_X_WINDOW (f))
 +    pgtk_clear_under_internal_border (f);
 +}
 +
 +/* Set the pixel height of the tool bar of frame F to HEIGHT.  */
 +static void
 +x_change_tool_bar_height (struct frame *f, int height)
 +{
 +  FRAME_TOOL_BAR_LINES (f) = 0;
 +  FRAME_TOOL_BAR_HEIGHT (f) = 0;
 +  if (height)
 +    {
 +      FRAME_EXTERNAL_TOOL_BAR (f) = true;
 +      if (FRAME_X_P (f) && f->output_data.pgtk->toolbar_widget == 0)
 +      /* Make sure next redisplay shows the tool bar.  */
 +      XWINDOW (FRAME_SELECTED_WINDOW (f))->update_mode_line = true;
 +      update_frame_tool_bar (f);
 +    }
 +  else
 +    {
 +      if (FRAME_EXTERNAL_TOOL_BAR (f))
 +      free_frame_tool_bar (f);
 +      FRAME_EXTERNAL_TOOL_BAR (f) = false;
 +    }
 +}
 +
 +
 +/* toolbar support */
 +static void
 +x_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
 +{
 +  int nlines;
 +
 +  /* Treat tool bars like menu bars.  */
 +  if (FRAME_MINIBUF_ONLY_P (f))
 +    return;
 +
 +  /* Use VALUE only if an int >= 0.  */
 +  if (RANGED_FIXNUMP (0, value, INT_MAX))
 +    nlines = XFIXNAT (value);
 +  else
 +    nlines = 0;
 +
 +  x_change_tool_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
 +
 +}
 +
 +static void
 +x_set_child_frame_border_width (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 +{
 +  int border = check_int_nonnegative (arg);
 +
 +  if (border != FRAME_CHILD_FRAME_BORDER_WIDTH (f))
 +    {
 +      f->child_frame_border_width = border;
 +
 +      if (FRAME_X_WINDOW (f))
 +      {
 +        adjust_frame_size (f, -1, -1, 3, false, Qchild_frame_border_width);
 +        pgtk_clear_under_internal_border (f);
 +      }
 +    }
 +
 +}
 +
 +
 +static void
 +x_set_internal_border_width (struct frame *f, Lisp_Object arg,
 +                           Lisp_Object oldval)
 +{
 +  int border = check_int_nonnegative (arg);
 +
 +  if (border != FRAME_INTERNAL_BORDER_WIDTH (f))
 +    {
 +      f->internal_border_width = border;
 +
 +      if (FRAME_X_WINDOW (f))
 +      {
 +        adjust_frame_size (f, -1, -1, 3, false, Qinternal_border_width);
 +        pgtk_clear_under_internal_border (f);
 +      }
 +    }
 +}
 +
 +
 +static void
 +x_set_icon_type (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 +{
 +  bool result;
 +
 +  if (STRINGP (arg))
 +    {
 +      if (STRINGP (oldval) && EQ (Fstring_equal (oldval, arg), Qt))
 +      return;
 +    }
 +  else if (!STRINGP (oldval) && NILP (oldval) == NILP (arg))
 +    return;
 +
 +  block_input ();
 +  if (NILP (arg))
 +    result = pgtk_text_icon (f,
 +                           SSDATA ((!NILP (f->icon_name)
 +                                    ? f->icon_name : f->name)));
 +  else
 +    result = FRAME_TERMINAL (f)->set_bitmap_icon_hook (f, arg);
 +
 +  if (result)
 +    {
 +      unblock_input ();
 +      error ("No icon window available");
 +    }
 +
 +  unblock_input ();
 +}
 +
 +
 +static void
 +x_set_icon_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 +{
 +  bool result;
 +
 +  if (STRINGP (arg))
 +    {
 +      if (STRINGP (oldval) && EQ (Fstring_equal (oldval, arg), Qt))
 +      return;
 +    }
 +  else if (!NILP (arg) || NILP (oldval))
 +    return;
 +
 +  fset_icon_name (f, arg);
 +
 +  block_input ();
 +
 +  result = pgtk_text_icon (f,
 +                         SSDATA ((!NILP (f->icon_name)
 +                                  ? f->icon_name
 +                                  : !NILP (f->title)
 +                                  ? f->title : f->name)));
 +
 +  if (result)
 +    {
 +      unblock_input ();
 +      error ("No icon window available");
 +    }
 +
 +  unblock_input ();
 +}
 +
 +/* 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 =
 +    gui_display_get_arg (dpyinfo, parms, Qicon_left, 0, 0, RES_TYPE_NUMBER);
 +  icon_y =
 +    gui_display_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_undecorated:
 + *
 + * Set frame F's `undecorated' parameter.  If non-nil, F's window-system
 + * window is drawn without decorations, title, minimize/maximize boxes
 + * and external borders.  This usually means that the window cannot be
 + * dragged, resized, iconified, maximized or deleted with the mouse.  If
 + * nil, draw the frame with all the elements listed above unless these
 + * have been suspended via window manager settings.
 + *
 + * Some window managers may not honor this parameter.
 + */
 +static void
 +x_set_undecorated (struct frame *f, Lisp_Object new_value,
 +                 Lisp_Object old_value)
 +{
 +  if (!EQ (new_value, old_value))
 +    {
 +      FRAME_UNDECORATED (f) = NILP (new_value) ? false : true;
 +      xg_set_undecorated (f, new_value);
 +    }
 +}
 +
 +/**
 + * x_set_skip_taskbar:
 + *
 + * Set frame F's `skip-taskbar' parameter.  If non-nil, this should
 + * remove F's icon from the taskbar associated with the display of F's
 + * window-system window and inhibit switching to F's window via
 + * <Alt>-<TAB>.  If nil, lift these restrictions.
 + *
 + * Some window managers may not honor this parameter.
 + */
 +static void
 +x_set_skip_taskbar (struct frame *f, Lisp_Object new_value,
 +                  Lisp_Object old_value)
 +{
 +  if (!EQ (new_value, old_value))
 +    {
 +      xg_set_skip_taskbar (f, new_value);
 +      FRAME_SKIP_TASKBAR (f) = !NILP (new_value);
 +    }
 +}
 +
 +/**
 + * 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.  */
 +      pgtk_make_frame_invisible (f);
 +
 +      xg_set_override_redirect (f, new_value);
 +
 +      pgtk_make_frame_visible (f);
 +      FRAME_OVERRIDE_REDIRECT (f) = !NILP (new_value);
 +    }
 +}
 +
 +/* Set icon from FILE for frame F.  By using GTK functions the icon
 +   may be any format that GdkPixbuf knows about, i.e. not just bitmaps.  */
 +
 +bool
 +xg_set_icon (struct frame *f, Lisp_Object file)
 +{
 +  bool result = false;
 +  Lisp_Object found;
 +
 +  if (!FRAME_GTK_OUTER_WIDGET (f))
 +    return false;
 +
 +  found = image_find_image_file (file);
 +
 +  if (!NILP (found))
 +    {
 +      GdkPixbuf *pixbuf;
 +      GError *err = NULL;
 +      char *filename = SSDATA (ENCODE_FILE (found));
 +      block_input ();
 +
 +      pixbuf = gdk_pixbuf_new_from_file (filename, &err);
 +
 +      if (pixbuf)
 +      {
 +        gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
 +                             pixbuf);
 +        g_object_unref (pixbuf);
 +
 +        result = true;
 +      }
 +      else
 +      g_error_free (err);
 +
 +      unblock_input ();
 +    }
 +
 +  return result;
 +}
 +
 +bool
 +xg_set_icon_from_xpm_data (struct frame *f, const char **data)
 +{
 +  GdkPixbuf *pixbuf = gdk_pixbuf_new_from_xpm_data (data);
 +
 +  if (!pixbuf)
 +    return false;
 +
 +  if (!FRAME_GTK_OUTER_WIDGET (f))
 +    return false;
 +
 +  gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), pixbuf);
 +  g_object_unref (pixbuf);
 +  return true;
 +}
 +
 +static void
 +pgtk_set_sticky (struct frame *f, Lisp_Object new_value,
 +               Lisp_Object old_value)
 +{
 +  if (!FRAME_GTK_OUTER_WIDGET (f))
 +    return;
 +
 +  if (!NILP (new_value))
 +    gtk_window_stick (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
 +  else
 +    gtk_window_unstick (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
 +}
 +
 +static void
 +pgtk_set_tool_bar_position (struct frame *f,
 +                          Lisp_Object new_value, Lisp_Object old_value)
 +{
 +  Lisp_Object choice = list4 (Qleft, Qright, Qtop, Qbottom);
 +
 +  if (!NILP (Fmemq (new_value, choice)))
 +    {
 +      if (!EQ (new_value, old_value))
 +      {
 +        xg_change_toolbar_position (f, new_value);
 +        fset_tool_bar_position (f, new_value);
 +      }
 +    }
 +  else
 +    wrong_choice (choice, new_value);
 +}
 +
 +static void
 +pgtk_set_scroll_bar_foreground (struct frame *f, Lisp_Object new_value,
 +                              Lisp_Object old_value)
 +{
 +  GtkCssProvider *css_provider =
 +    FRAME_X_OUTPUT (f)->scrollbar_foreground_css_provider;
 +
 +  if (NILP (new_value))
 +    {
 +      gtk_css_provider_load_from_data (css_provider, "", -1, NULL);
 +      update_face_from_frame_parameter (f, Qscroll_bar_foreground, new_value);
 +    }
 +  else if (STRINGP (new_value))
 +    {
 +      Emacs_Color rgb;
 +
 +      if (!pgtk_parse_color (f, SSDATA (new_value), &rgb))
 +      error ("Unknown color.");
 +
 +      /* On pgtk, this frame parameter should be ignored, and honor gtk theme. */
 +#if 0
 +      char css[64];
 +      sprintf (css, "scrollbar slider { background-color: #%06x; }",
 +             (unsigned int) rgb.pixel & 0xffffff);
 +      gtk_css_provider_load_from_data (css_provider, css, -1, NULL);
 +#endif
 +      update_face_from_frame_parameter (f, Qscroll_bar_foreground, new_value);
 +
 +    }
 +  else
 +    error ("Invalid scroll-bar-foreground.");
 +}
 +
 +static void
 +pgtk_set_scroll_bar_background (struct frame *f, Lisp_Object new_value,
 +                              Lisp_Object old_value)
 +{
 +  GtkCssProvider *css_provider =
 +    FRAME_X_OUTPUT (f)->scrollbar_background_css_provider;
 +
 +  if (NILP (new_value))
 +    {
 +      gtk_css_provider_load_from_data (css_provider, "", -1, NULL);
 +      update_face_from_frame_parameter (f, Qscroll_bar_background, new_value);
 +    }
 +  else if (STRINGP (new_value))
 +    {
 +      Emacs_Color rgb;
 +
 +      if (!pgtk_parse_color (f, SSDATA (new_value), &rgb))
 +      error ("Unknown color.");
 +
 +      /* On pgtk, this frame parameter should be ignored, and honor gtk theme. */
 +#if 0
 +      char css[64];
 +      sprintf (css, "scrollbar trough { background-color: #%06x; }",
 +             (unsigned int) rgb.pixel & 0xffffff);
 +      gtk_css_provider_load_from_data (css_provider, css, -1, NULL);
 +#endif
 +      update_face_from_frame_parameter (f, Qscroll_bar_background, new_value);
 +
 +    }
 +  else
 +    error ("Invalid scroll-bar-background.");
 +}
 +
 +\f
 +/***********************************************************************
 +                             Printing
 + ***********************************************************************/
 +
 +
 +DEFUN ("x-export-frames", Fx_export_frames, Sx_export_frames, 0, 2, 0,
 +       doc: /* Return image data of FRAMES in TYPE format.
 +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.  Optional arg TYPE should be either `pdf' (default), `png',
 +`postscript', or `svg'.  Supported types are determined by the
 +compile-time configuration of cairo.
 +
 +Note: Text drawn with the `x' font backend is shown with hollow boxes
 +unless TYPE is `png'.  */)
 +     (Lisp_Object frames, Lisp_Object type)
 +{
 +  Lisp_Object rest, tmp;
 +  cairo_surface_type_t surface_type;
 +
 +  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 exported must be visible.");
 +      tmp = Fcons (frame, tmp);
 +    }
 +  frames = Fnreverse (tmp);
 +
 +#ifdef CAIRO_HAS_PDF_SURFACE
 +  if (NILP (type) || EQ (type, Qpdf))
 +    surface_type = CAIRO_SURFACE_TYPE_PDF;
 +  else
 +#endif
 +#ifdef CAIRO_HAS_PNG_FUNCTIONS
 +  if (EQ (type, Qpng))
 +    {
 +      if (!NILP (XCDR (frames)))
 +      error ("PNG export cannot handle multiple frames.");
 +      surface_type = CAIRO_SURFACE_TYPE_IMAGE;
 +    }
 +  else
 +#endif
 +#ifdef CAIRO_HAS_PS_SURFACE
 +  if (EQ (type, Qpostscript))
 +    surface_type = CAIRO_SURFACE_TYPE_PS;
 +  else
 +#endif
 +#ifdef CAIRO_HAS_SVG_SURFACE
 +  if (EQ (type, Qsvg))
 +    {
 +      /* For now, we stick to SVG 1.1.  */
 +      if (!NILP (XCDR (frames)))
 +      error ("SVG export cannot handle multiple frames.");
 +      surface_type = CAIRO_SURFACE_TYPE_SVG;
 +    }
 +  else
 +#endif
 +    error ("Unsupported export type");
 +
 +  return pgtk_cr_export_frames (frames, surface_type);
 +}
 +
 +
 +/* Note: see frame.c for template, also where generic functions are impl */
 +frame_parm_handler pgtk_frame_parm_handlers[] = {
 +  gui_set_autoraise,          /* generic OK */
 +  gui_set_autolower,          /* generic OK */
 +  x_set_background_color,
 +  x_set_border_color,
 +  gui_set_border_width,
 +  x_set_cursor_color,
 +  x_set_cursor_type,
 +  gui_set_font,                       /* generic OK */
 +  x_set_foreground_color,
 +  x_set_icon_name,
 +  x_set_icon_type,
 +  x_set_child_frame_border_width,
 +  x_set_internal_border_width,        /* generic OK */
 +  gui_set_right_divider_width,
 +  gui_set_bottom_divider_width,
 +  x_set_menu_bar_lines,
 +  x_set_mouse_color,
 +  x_explicitly_set_name,
 +  gui_set_scroll_bar_width,   /* generic OK */
 +  gui_set_scroll_bar_height,  /* generic OK */
 +  x_set_title,
 +  gui_set_unsplittable,               /* generic OK */
 +  gui_set_vertical_scroll_bars,       /* generic OK */
 +  gui_set_horizontal_scroll_bars,     /* generic OK */
 +  gui_set_visibility,         /* generic OK */
 +  x_set_tab_bar_lines,
 +  x_set_tool_bar_lines,
 +  pgtk_set_scroll_bar_foreground,
 +  pgtk_set_scroll_bar_background,
 +  gui_set_screen_gamma,               /* generic OK */
 +  gui_set_line_spacing,               /* generic OK, sets f->extra_line_spacing to int */
 +  gui_set_left_fringe,                /* generic OK */
 +  gui_set_right_fringe,               /* generic OK */
 +  0,                          /* x_set_wait_for_wm */
 +  gui_set_fullscreen,         /* generic OK */
 +  gui_set_font_backend,               /* generic OK */
 +  gui_set_alpha,
 +  pgtk_set_sticky,
 +  pgtk_set_tool_bar_position,
 +  0,                          /* x_set_inhibit_double_buffering */
 +  x_set_undecorated,
 +  x_set_parent_frame,
 +  x_set_skip_taskbar,
 +  x_set_no_focus_on_map,
 +  x_set_no_accept_focus,
 +  x_set_z_group,
 +  x_set_override_redirect,
 +  gui_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);
 +}
 +
 +void
 +pgtk_default_font_parameter (struct frame *f, Lisp_Object parms)
 +{
 +  struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
 +  Lisp_Object font_param =
 +    gui_display_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
 +      : gui_display_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);
 +      gui_set_frame_parameters (f, arg);
 +    }
 +
 +  /* This call will make X resources override any system font setting.  */
 +  gui_default_parameter (f, parms, Qfont, font, "font", "Font",
 +                       RES_TYPE_STRING);
 +}
 +
 +/* ==========================================================================
 +
 +    Lisp definitions
 +
 +   ========================================================================== */
 +
 +DEFUN ("pgtk-set-monitor-scale-factor", Fpgtk_set_monitor_scale_factor,
 +       Spgtk_set_monitor_scale_factor, 2, 2, 0,
 +       doc: /* Set monitor MONITOR-MODEL's scale factor to SCALE-FACTOR.
 +Since Gdk's scale factor is integer, physical pixel width/height is
 +incorrect when you specify fractional scale factor in compositor.
 +If you set scale factor by this function, it is used instead of Gdk's one.
 +
 +Pass nil as SCALE-FACTOR if you want to reset the specified monitor's
 +scale factor. */ )
 +  (Lisp_Object monitor_model, Lisp_Object scale_factor)
 +{
 +  CHECK_STRING (monitor_model);
 +  if (!NILP (scale_factor))
 +    {
 +      CHECK_NUMBER (scale_factor);
 +      if (FIXNUMP (scale_factor))
 +      {
 +        if (XFIXNUM (scale_factor) <= 0)
 +          error ("scale factor must be > 0.");
 +      }
 +      else if (FLOATP (scale_factor))
 +      {
 +        if (XFLOAT_DATA (scale_factor) <= 0.0)
 +          error ("scale factor must be > 0.");
 +      }
 +      else
 +      error ("unknown type of scale-factor");
 +    }
 +
 +  Lisp_Object tem = Fassoc (monitor_model, monitor_scale_factor_alist, Qnil);
 +  if (NILP (tem))
 +    {
 +      if (!NILP (scale_factor))
 +      monitor_scale_factor_alist = Fcons (Fcons (monitor_model, scale_factor),
 +                                          monitor_scale_factor_alist);
 +    }
 +  else
 +    {
 +      Fsetcdr (tem, scale_factor);
 +    }
 +
 +  return scale_factor;
 +}
 +
 +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;
-     gui_figure_window_size (f, parms, true, true, &x_width, &x_height);
 +
 +  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 =
 +    gui_display_get_arg (dpyinfo, parms, Qterminal, 0, 0, RES_TYPE_NUMBER);
 +  if (EQ (display, Qunbound))
 +    display =
 +      gui_display_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 =
 +    gui_display_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 =
 +    gui_display_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 =
 +    gui_display_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 =
 +    gui_display_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 =
 +       (gui_display_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 =
 +       (gui_display_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;
 +  FRAME_X_OUTPUT (f)->white_relief.pixel = -1;
 +  FRAME_X_OUTPUT (f)->black_relief.pixel = -1;
 +
 +  FRAME_X_OUTPUT (f)->scrollbar_foreground_css_provider =
 +    gtk_css_provider_new ();
 +  FRAME_X_OUTPUT (f)->scrollbar_background_css_provider =
 +    gtk_css_provider_new ();
 +
 +  fset_icon_name (f,
 +                gui_display_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;
 +    FRAME_X_OUTPUT (f)->cursor_foreground_color = -1;
 +    FRAME_X_OUTPUT (f)->border_pixel = -1;
 +    FRAME_X_OUTPUT (f)->mouse_color = -1;
 +
 +    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));
 +    FRAME_X_OUTPUT (f)->cursor_foreground_color
 +      = 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_color
 +      = x_decode_color (f, black, BLACK_PIX_DEFAULT (f));
 +  }
 +
 +  /* 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);
 +#ifdef HAVE_HARFBUZZ
 +  register_font_driver (&ftcrhbfont_driver, f);
 +#endif        /* HAVE_HARFBUZZ */
 +
 +  image_cache_refcount =
 +    FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0;
 +
 +  gui_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.  */
 +  pgtk_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
 +    gui_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 = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width,
 +                                 "internalBorder", "internalBorder",
 +                                 RES_TYPE_NUMBER);
 +      if (!EQ (value, Qunbound))
 +      parms = Fcons (Fcons (Qinternal_border_width, value), parms);
 +    }
 +
 +  /* Same for child frames.  */
 +  if (NILP (Fassq (Qchild_frame_border_width, parms)))
 +    {
 +      Lisp_Object value;
 +
 +      value = gui_display_get_arg (dpyinfo, parms, Qchild_frame_border_width,
 +                                   "childFrameBorderWidth", "childFrameBorderWidth",
 +                                   RES_TYPE_NUMBER);
 +      if (! EQ (value, Qunbound))
 +      parms = Fcons (Fcons (Qchild_frame_border_width, value),
 +                     parms);
 +
 +    }
 +
 +  gui_default_parameter (f, parms, Qchild_frame_border_width,
 +                       make_fixnum (0),
 +                       "childFrameBorderWidth", "childFrameBorderWidth",
 +                       RES_TYPE_NUMBER);
 +  gui_default_parameter (f, parms, Qinternal_border_width,
 +                       make_fixnum (0),
 +                       "internalBorderWidth", "internalBorderWidth",
 +                       RES_TYPE_NUMBER);
 +  gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0),
 +                       NULL, NULL, RES_TYPE_NUMBER);
 +  gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
 +                       NULL, NULL, RES_TYPE_NUMBER);
 +  gui_default_parameter (f, parms, Qvertical_scroll_bars,
 +                       Qright,
 +                       "verticalScrollBars", "ScrollBars", RES_TYPE_SYMBOL);
 +  gui_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.  */
 +  gui_default_parameter (f, parms, Qforeground_color, build_string ("black"),
 +                       "foreground", "Foreground", RES_TYPE_STRING);
 +  gui_default_parameter (f, parms, Qbackground_color, build_string ("white"),
 +                       "background", "Background", RES_TYPE_STRING);
 +  gui_default_parameter (f, parms, Qmouse_color, build_string ("black"),
 +                       "pointerColor", "Foreground", RES_TYPE_STRING);
 +  gui_default_parameter (f, parms, Qborder_color, build_string ("black"),
 +                       "borderColor", "BorderColor", RES_TYPE_STRING);
 +  gui_default_parameter (f, parms, Qscreen_gamma, Qnil,
 +                       "screenGamma", "ScreenGamma", RES_TYPE_FLOAT);
 +  gui_default_parameter (f, parms, Qline_spacing, Qnil,
 +                       "lineSpacing", "LineSpacing", RES_TYPE_NUMBER);
 +  gui_default_parameter (f, parms, Qleft_fringe, Qnil,
 +                       "leftFringe", "LeftFringe", RES_TYPE_NUMBER);
 +  gui_default_parameter (f, parms, Qright_fringe, Qnil,
 +                       "rightFringe", "RightFringe", RES_TYPE_NUMBER);
 +  gui_default_parameter (f, parms, Qno_special_glyphs, Qnil,
 +                       NULL, NULL, RES_TYPE_BOOLEAN);
 +
 +  gui_default_parameter (f, parms, Qscroll_bar_foreground, Qnil,
 +                       "scrollBarForeground", "ScrollBarForeground",
 +                       RES_TYPE_STRING);
 +  gui_default_parameter (f, parms, Qscroll_bar_background, Qnil,
 +                       "scrollBarBackground", "ScrollBarBackground",
 +                       RES_TYPE_STRING);
 +
 +  /* Init faces before gui_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 =
 +    gui_display_get_arg (dpyinfo, parms, Qmin_width, NULL, NULL,
 +                       RES_TYPE_NUMBER);
 +  if (NUMBERP (tem))
 +    store_frame_param (f, Qmin_width, tem);
 +  tem =
 +    gui_display_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.  */
 +
 +  gui_default_parameter (f, parms, Qmenu_bar_lines,
 +                       NILP (Vmenu_bar_mode)
 +                       ? make_fixnum (0) : make_fixnum (1),
 +                       NULL, NULL, RES_TYPE_NUMBER);
 +  gui_default_parameter (f, parms, Qtab_bar_lines,
 +                       NILP (Vtab_bar_mode)
 +                       ? make_fixnum (0) : make_fixnum (1),
 +                       NULL, NULL, RES_TYPE_NUMBER);
 +  gui_default_parameter (f, parms, Qtool_bar_lines,
 +                       NILP (Vtool_bar_mode)
 +                       ? make_fixnum (0) : make_fixnum (1),
 +                       NULL, NULL, RES_TYPE_NUMBER);
 +
 +  gui_default_parameter (f, parms, Qbuffer_predicate, Qnil,
 +                       "bufferPredicate", "BufferPredicate",
 +                       RES_TYPE_SYMBOL);
 +  gui_default_parameter (f, parms, Qtitle, Qnil,
 +                       "title", "Title", RES_TYPE_STRING);
 +  gui_default_parameter (f, parms, Qwait_for_wm, Qt,
 +                       "waitForWM", "WaitForWM", RES_TYPE_BOOLEAN);
 +  gui_default_parameter (f, parms, Qtool_bar_position,
 +                       FRAME_TOOL_BAR_POSITION (f), 0, 0, RES_TYPE_SYMBOL);
 +  gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil,
 +                       "inhibitDoubleBuffering", "InhibitDoubleBuffering",
 +                       RES_TYPE_BOOLEAN);
 +
 +  /* Compute the size of the X window.  */
 +  window_prompting =
-   if (x_width > 0)
-     SET_FRAME_WIDTH (f, x_width);
-   if (x_height > 0)
-     SET_FRAME_HEIGHT (f, x_height);
++    gui_figure_window_size (f, parms, true, true);
 +
 +  tem =
 +    gui_display_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.  */
 +  gui_default_parameter (f, parms, Qicon_type, Qt,
 +                       "bitmapIcon", "BitmapIcon", RES_TYPE_BOOLEAN);
 +
 +  gui_default_parameter (f, parms, Qauto_raise, Qnil,
 +                       "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN);
 +  gui_default_parameter (f, parms, Qauto_lower, Qnil,
 +                       "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN);
 +  gui_default_parameter (f, parms, Qcursor_type, Qbox,
 +                       "cursorType", "CursorType", RES_TYPE_SYMBOL);
 +  gui_default_parameter (f, parms, Qscroll_bar_width, Qnil,
 +                       "scrollBarWidth", "ScrollBarWidth", RES_TYPE_NUMBER);
 +  gui_default_parameter (f, parms, Qscroll_bar_height, Qnil,
 +                       "scrollBarHeight", "ScrollBarHeight",
 +                       RES_TYPE_NUMBER);
 +  gui_default_parameter (f, parms, Qalpha, Qnil,
 +                       "alpha", "Alpha", RES_TYPE_NUMBER);
 +
 +  if (!NILP (parent_frame))
 +    {
 +      struct frame *p = XFRAME (parent_frame);
 +
 +      block_input ();
 +
 +      PGTK_TRACE ("x_set_parent_frame x: %d, y: %d", f->left_pos, f->top_pos);
 +      GtkWidget *fixed = FRAME_GTK_WIDGET (f);
 +      GtkWidget *fixed_of_p = FRAME_GTK_WIDGET (p);
 +      GtkWidget *whbox_of_f = gtk_widget_get_parent (fixed);
 +      g_object_ref (fixed);
 +      gtk_container_remove (GTK_CONTAINER (whbox_of_f), fixed);
 +      gtk_fixed_put (GTK_FIXED (fixed_of_p), fixed, f->left_pos, f->top_pos);
 +      gtk_widget_show_all (fixed);
 +      g_object_unref (fixed);
 +
 +      gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
 +      FRAME_GTK_OUTER_WIDGET (f) = NULL;
 +      FRAME_OUTPUT_DATA (f)->vbox_widget = NULL;
 +      FRAME_OUTPUT_DATA (f)->hbox_widget = NULL;
 +      FRAME_OUTPUT_DATA (f)->menubar_widget = NULL;
 +      FRAME_OUTPUT_DATA (f)->toolbar_widget = NULL;
 +      FRAME_OUTPUT_DATA (f)->ttip_widget = NULL;
 +      FRAME_OUTPUT_DATA (f)->ttip_lbl = NULL;
 +      FRAME_OUTPUT_DATA (f)->ttip_window = NULL;
 +
 +      unblock_input ();
 +    }
 +
 +  if (FRAME_GTK_OUTER_WIDGET (f))
 +    gtk_widget_show_all (FRAME_GTK_OUTER_WIDGET (f));
 +
 +  gui_default_parameter (f, parms, Qno_focus_on_map, Qnil,
 +                       NULL, NULL, RES_TYPE_BOOLEAN);
 +  gui_default_parameter (f, parms, Qno_accept_focus, Qnil,
 +                       NULL, NULL, RES_TYPE_BOOLEAN);
 +
 +  /* Create the menu bar.  */
 +  if (!minibuffer_only && FRAME_EXTERNAL_MENU_BAR (f))
 +    {
 +      /* 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);
 +
 +    }
 +
 +  /* Consider frame official, now.  */
 +  f->can_set_window_size = true;
 +
-   int width, 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.  */
 +  gui_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
 +      =
 +      gui_display_get_arg (dpyinfo, parms, Qvisibility, 0, 0,
 +                           RES_TYPE_SYMBOL);
 +
 +      if (EQ (visibility, Qicon))
 +      pgtk_iconify_frame (f);
 +      else
 +      {
 +        if (EQ (visibility, Qunbound))
 +          visibility = Qt;
 +
 +        if (!NILP (visibility))
 +          pgtk_make_frame_visible (f);
 +      }
 +
 +      store_frame_param (f, Qvisibility, visibility);
 +    }
 +
 +  /* Works iff frame has been already mapped.  */
 +  gui_default_parameter (f, parms, Qskip_taskbar, Qnil,
 +                       NULL, NULL, RES_TYPE_BOOLEAN);
 +  /* The `z-group' parameter works only for visible frames.  */
 +  gui_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 gui_display_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)->border_color_css_provider = NULL;
 +
 +  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);
 +}
 +
 +
 +#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
 +
 +/**
 + * x_frame_restack:
 + *
 + * Restack frame F1 below frame F2, above if ABOVE_FLAG is non-nil.  In
 + * practice this is a two-step action: The first step removes F1's
 + * window-system window from the display.  The second step reinserts
 + * F1's window below (above if ABOVE_FLAG is true) that of F2.
 + */
 +static void
 +pgtk_frame_restack (struct frame *f1, struct frame *f2, bool above_flag)
 +{
 +  block_input ();
 +  xg_frame_restack (f1, f2, above_flag);
 +  unblock_input ();
 +}
 +
 +
 +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.
 +
 +This may be thought of as an atomic action performed in two steps: The
 +first step removes FRAME1's window-step window from the display.  The
 +second step reinserts FRAME1's window below (above if ABOVE is true)
 +that of FRAME2.  Hence the position of FRAME2 in its display's Z
 +\(stacking) order relative to all other frames excluding FRAME1 remains
 +unaltered.
 +
 +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_GTK_OUTER_WIDGET (f1) && FRAME_GTK_OUTER_WIDGET (f2)))
 +    error ("Cannot restack frames");
 +  pgtk_frame_restack (f1, f2, !NILP (above));
 +  return Qt;
 +}
 +
 +DEFUN ("pgtk-popup-font-panel", Fpgtk_popup_font_panel, Spgtk_popup_font_panel,
 +       0, 1, "",
 +       doc: /* Pop up the font panel.  */)
 +     (Lisp_Object frame)
 +{
 +  struct frame *f = decode_window_system_frame (frame);
 +
 +  Lisp_Object font;
 +  Lisp_Object font_param;
 +  char *default_name = NULL;
 +  ptrdiff_t count = SPECPDL_INDEX ();
 +
 +  block_input ();
 +
 +  XSETFONT (font, FRAME_FONT (f));
 +  font_param = Ffont_get (font, QCname);
 +  if (STRINGP (font_param))
 +    default_name = xlispstrdup (font_param);
 +  else
 +    {
 +      font_param = Fframe_parameter (frame, Qfont_parameter);
 +      if (STRINGP (font_param))
 +        default_name = xlispstrdup (font_param);
 +    }
 +
 +  font = xg_get_font (f, default_name);
 +  xfree (default_name);
 +
 +  unblock_input ();
 +
 +  if (NILP (font))
 +    quit ();
 +
 +  return unbind_to (count, font);
 +}
 +
 +
 +
 +#ifdef HAVE_GSETTINGS
 +
 +#define RESOURCE_KEY_MAX_LEN 128
 +#define SCHEMA_ID "org.gnu.emacs.defaults"
 +#define PATH_FOR_CLASS_TYPE "/org/gnu/emacs/defaults-by-class/"
 +#define PATH_PREFIX_FOR_NAME_TYPE "/org/gnu/emacs/defaults-by-name/"
 +
 +static inline int
 +pgtk_is_lower_char (int c)
 +{
 +  return c >= 'a' && c <= 'z';
 +}
 +
 +static inline int
 +pgtk_is_upper_char (int c)
 +{
 +  return c >= 'A' && c <= 'Z';
 +}
 +
 +static inline int
 +pgtk_is_numeric_char (int c)
 +{
 +  return c >= '0' && c <= '9';
 +}
 +
 +static GSettings *
 +parse_resource_key (const char *res_key, char *setting_key)
 +{
 +  char path[32 + RESOURCE_KEY_MAX_LEN];
 +  const char *sp = res_key;
 +  char *dp;
 +
 +  /*
 +   * res_key="emacs.cursorBlink"
 +   *   -> path="/org/gnu/emacs/defaults-by-name/emacs/"
 +   *      setting_key="cursor-blink"
 +   *
 +   * res_key="Emacs.CursorBlink"
 +   *   -> path="/org/gnu/emacs/defaults-by-class/"
 +   *      setting_key="cursor-blink"
 +   *
 +   * Returns GSettings* if setting_key exists in schema, otherwise NULL.
 +   */
 +
 +  /* generate path */
 +  if (pgtk_is_upper_char (*sp))
 +    {
 +      /* First letter is upper case. It should be "Emacs",
 +       * but don't care.
 +       */
 +      strcpy (path, PATH_FOR_CLASS_TYPE);
 +      while (*sp != '\0')
 +      {
 +        if (*sp == '.')
 +          break;
 +        sp++;
 +      }
 +    }
 +  else
 +    {
 +      strcpy (path, PATH_PREFIX_FOR_NAME_TYPE);
 +      dp = path + strlen (path);
 +      while (*sp != '\0')
 +      {
 +        int c = *sp;
 +        if (c == '.')
 +          break;
 +        if (pgtk_is_lower_char (c))
 +          (void) 0;           /* lower -> NOP */
 +        else if (pgtk_is_upper_char (c))
 +          c = c - 'A' + 'a';  /* upper -> lower */
 +        else if (pgtk_is_numeric_char (c))
 +          (void) 0;           /* numeric -> NOP */
 +        else
 +          return NULL;        /* invalid */
 +        *dp++ = c;
 +        sp++;
 +      }
 +      *dp++ = '/';            /* must ends with '/' */
 +      *dp = '\0';
 +    }
 +
 +  if (*sp++ != '.')
 +    return NULL;
 +
 +  /* generate setting_key */
 +  dp = setting_key;
 +  while (*sp != '\0')
 +    {
 +      int c = *sp;
 +      if (pgtk_is_lower_char (c))
 +      (void) 0;               /* lower -> NOP */
 +      else if (pgtk_is_upper_char (c))
 +      {
 +        c = c - 'A' + 'a';    /* upper -> lower */
 +        if (dp != setting_key)
 +          *dp++ = '-';        /* store '-' unless first char */
 +      }
 +      else if (pgtk_is_numeric_char (c))
 +      (void) 0;               /* numeric -> NOP */
 +      else
 +      return NULL;            /* invalid */
 +
 +      *dp++ = c;
 +      sp++;
 +    }
 +  *dp = '\0';
 +
 +  /* check existence of setting_key */
 +  GSettingsSchemaSource *ssrc = g_settings_schema_source_get_default ();
 +  GSettingsSchema *scm = g_settings_schema_source_lookup (ssrc, SCHEMA_ID, FALSE);
 +  if (!scm)
 +    return NULL;      /* *.schema.xml is not installed. */
 +  if (!g_settings_schema_has_key (scm, setting_key))
 +    {
 +      g_settings_schema_unref (scm);
 +      return NULL;
 +    }
 +
 +  /* create GSettings, and return it */
 +  GSettings *gs = g_settings_new_full (scm, NULL, path);
 +
 +  g_settings_schema_unref (scm);
 +  return gs;
 +}
 +
 +const char *
 +pgtk_get_defaults_value (const char *key)
 +{
 +  char skey[(RESOURCE_KEY_MAX_LEN + 1) * 2];
 +
 +  if (strlen (key) >= RESOURCE_KEY_MAX_LEN)
 +    error ("resource key too long.");
 +
 +  GSettings *gs = parse_resource_key (key, skey);
 +  if (gs == NULL)
 +    {
 +      return NULL;
 +    }
 +
 +  gchar *str = g_settings_get_string (gs, skey);
 +
 +  /* There is no timing to free str.
 +   * So, copy it here and free it.
 +   *
 +   * MEMO: Resource values for emacs shouldn't need such a long string value.
 +   */
 +  static char holder[128];
 +  strncpy (holder, str, 128);
 +  holder[127] = '\0';
 +
 +  g_object_unref (gs);
 +  g_free (str);
 +  return holder[0] != '\0' ? holder : NULL;
 +}
 +
 +static void
 +pgtk_set_defaults_value (const char *key, const char *value)
 +{
 +  char skey[(RESOURCE_KEY_MAX_LEN + 1) * 2];
 +
 +  if (strlen (key) >= RESOURCE_KEY_MAX_LEN)
 +    error ("resource key too long.");
 +
 +  GSettings *gs = parse_resource_key (key, skey);
 +  if (gs == NULL)
 +    error ("unknown resource key.");
 +
 +  if (value != NULL)
 +    {
 +      g_settings_set_string (gs, skey, value);
 +    }
 +  else
 +    {
 +      g_settings_reset (gs, skey);
 +    }
 +
 +  g_object_unref (gs);
 +}
 +
 +#undef RESOURCE_KEY_MAX_LEN
 +#undef SCHEMA_ID
 +#undef PATH_FOR_CLASS_TYPE
 +#undef PATH_PREFIX_FOR_NAME_TYPE
 +
 +#else /* not HAVE_GSETTINGS */
 +
 +const char *
 +pgtk_get_defaults_value (const char *key)
 +{
 +  return NULL;
 +}
 +
 +static void
 +pgtk_set_defaults_value (const char *key, const char *value)
 +{
 +  error ("gsettings not supported.");
 +}
 +
 +#endif
 +
 +
 +DEFUN ("pgtk-set-resource", Fpgtk_set_resource, Spgtk_set_resource, 2, 2, 0,
 +       doc: /* Set the value of ATTRIBUTE, of class CLASS, as VALUE, into defaults database. */ )
 +  (Lisp_Object attribute, Lisp_Object value)
 +{
 +  check_window_system (NULL);
 +
 +  CHECK_STRING (attribute);
 +  if (!NILP (value))
 +    CHECK_STRING (value);
 +
 +  char *res = SSDATA (Vx_resource_name);
 +  char *attr = SSDATA (attribute);
 +  if (attr[0] >= 'A' && attr[0] <= 'Z')
 +    res = SSDATA (Vx_resource_class);
 +
 +  char *key = g_strdup_printf ("%s.%s", res, attr);
 +
 +  pgtk_set_defaults_value (key, NILP (value) ? NULL : SSDATA (value));
 +
 +  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;
 +  gint n_monitors, i;
 +  int height_mm_at_0 = 0, height_mm_at_other = 0;
 +
 +  block_input ();
 +  gdpy = dpyinfo->gdpy;
 +  n_monitors = gdk_display_get_n_monitors (gdpy);
 +
 +  for (i = 0; i < n_monitors; ++i)
 +    {
 +      GdkRectangle rec;
 +
 +      GdkMonitor *monitor = gdk_display_get_monitor (gdpy, i);
 +      gdk_monitor_get_geometry (monitor, &rec);
 +
 +      int mm = gdk_monitor_get_height_mm (monitor);
 +
 +      if (rec.y == 0)
 +      height_mm_at_0 = max(height_mm_at_0, mm);
 +      else
 +      height_mm_at_other += mm;
 +    }
 +
 +  unblock_input ();
 +
 +  return make_fixnum (height_mm_at_0 + height_mm_at_other);
 +}
 +
 +
 +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;
 +  gint n_monitors, i;
 +  int width_mm_at_0 = 0, width_mm_at_other = 0;
 +
 +  block_input ();
 +  gdpy = dpyinfo->gdpy;
 +  n_monitors = gdk_display_get_n_monitors (gdpy);
 +
 +  for (i = 0; i < n_monitors; ++i)
 +    {
 +      GdkRectangle rec;
 +
 +      GdkMonitor *monitor = gdk_display_get_monitor (gdpy, i);
 +      gdk_monitor_get_geometry (monitor, &rec);
 +
 +      int mm = gdk_monitor_get_width_mm (monitor);
 +
 +      if (rec.x == 0)
 +      width_mm_at_0 = max(width_mm_at_0, mm);
 +      else
 +      width_mm_at_other += mm;
 +    }
 +
 +  unblock_input ();
 +
 +  return make_fixnum (width_mm_at_0 + width_mm_at_other);
 +}
 +
 +
 +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
 +pgtk_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
 +pgtk_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 */
 +const char *
 +pgtk_get_string_resource (XrmDatabase rdb, const char *name,
 +                        const char *class)
 +{
 +  check_window_system (NULL);
 +
 +  if (inhibit_x_resources)
 +    /* --quick was passed, so this is a no-op.  */
 +    return NULL;
 +
 +  const char *res = pgtk_get_defaults_value (name);
 +  if (res == NULL)
 +    res = pgtk_get_defaults_value (class);
 +
 +  if (res == NULL)
 +    return NULL;
 +
 +  if (c_strncasecmp (res, "YES", 3) == 0)
 +    return "true";
 +
 +  if (c_strncasecmp (res, "NO", 2) == 0)
 +    return "false";
 +
 +  return 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;
 +  struct frame *f = decode_window_system_frame (frame);
 +
 +  CHECK_STRING (color);
 +
 +  if (pgtk_defined_color (f, SSDATA (color), &col, false, false))
 +    return Qt;
 +  else
 +    return Qnil;
 +}
 +
 +
 +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;
 +  struct frame *f = decode_window_system_frame (frame);
 +
 +  CHECK_STRING (color);
 +
 +  if (pgtk_defined_color (f, SSDATA (color), &col, false, false))
 +    return list3i (col.red, col.green, col.blue);
 +  else
 +    return Qnil;
 +}
 +
 +
 +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);
 +  GdkDisplay *gdpy;
 +  gint n_monitors, i;
 +  int width = 0;
 +
 +  block_input ();
 +  gdpy = dpyinfo->gdpy;
 +  n_monitors = gdk_display_get_n_monitors (gdpy);
 +
 +  for (i = 0; i < n_monitors; ++i)
 +    {
 +      GdkRectangle rec;
 +      double scale = 1;
 +
 +      GdkMonitor *monitor = gdk_display_get_monitor (gdpy, i);
 +      gdk_monitor_get_geometry (monitor, &rec);
 +
 +      /* GTK returns scaled sizes for the workareas.  */
 +      scale = pgtk_get_monitor_scale_factor (gdk_monitor_get_model (monitor));
 +      if (scale == 0.0)
 +      scale = gdk_monitor_get_scale_factor (monitor);
 +      rec.x = rec.x * scale + 0.5;
 +      rec.y = rec.y * scale + 0.5;
 +      rec.width = rec.width * scale + 0.5;
 +      rec.height = rec.height * scale + 0.5;
 +
 +      width = max(width, rec.x + rec.width);
 +    }
 +
 +  unblock_input ();
 +
 +  return make_fixnum (width);
 +}
 +
 +
 +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);
 +  GdkDisplay *gdpy;
 +  gint n_monitors, i;
 +  int height = 0;
 +
 +  block_input ();
 +  gdpy = dpyinfo->gdpy;
 +  n_monitors = gdk_display_get_n_monitors (gdpy);
 +
 +  for (i = 0; i < n_monitors; ++i)
 +    {
 +      GdkRectangle rec;
 +      double scale = 1;
 +
 +      GdkMonitor *monitor = gdk_display_get_monitor (gdpy, i);
 +      gdk_monitor_get_geometry (monitor, &rec);
 +
 +      /* GTK returns scaled sizes for the workareas.  */
 +      scale = pgtk_get_monitor_scale_factor (gdk_monitor_get_model (monitor));
 +      if (scale == 0.0)
 +      scale = gdk_monitor_get_scale_factor (monitor);
 +      rec.x = rec.x * scale + 0.5;
 +      rec.y = rec.y * scale + 0.5;
 +      rec.width = rec.width * scale + 0.5;
 +      rec.height = rec.height * scale + 0.5;
 +
 +      height = max(height, rec.y + rec.height);
 +    }
 +
 +  unblock_input ();
 +
 +  return make_fixnum (height);
 +}
 +
 +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.
 +
 +In addition to the standard attribute keys listed in
 +`display-monitor-attributes-list', the following keys are contained in
 +the attributes:
 +
 + source -- String describing the source from which multi-monitor
 +         information is obtained, \"Gdk\"
 +
 +Internal use only, use `display-monitor-attributes-list' instead.  */)
 +  (Lisp_Object terminal)
 +{
 +  struct pgtk_display_info *dpyinfo = check_pgtk_display_info (terminal);
 +  Lisp_Object attributes_list = Qnil;
 +
 +  GdkDisplay *gdpy;
 +  gint primary_monitor = 0, n_monitors, i;
 +  Lisp_Object monitor_frames, rest, frame;
 +  static const char *source = "Gdk";
 +  struct MonitorInfo *monitors;
 +
 +  block_input ();
 +  gdpy = dpyinfo->gdpy;
 +  n_monitors = gdk_display_get_n_monitors (gdpy);
 +  monitor_frames = make_nil_vector (n_monitors);
 +  monitors = xzalloc (n_monitors * sizeof *monitors);
 +
 +  FOR_EACH_FRAME (rest, frame)
 +    {
 +      struct frame *f = XFRAME (frame);
 +
 +      if (FRAME_PGTK_P (f)
 +        && FRAME_DISPLAY_INFO (f) == dpyinfo
 +        && !FRAME_TOOLTIP_P (f))
 +      {
 +        GdkWindow *gwin = gtk_widget_get_window (FRAME_GTK_WIDGET (f));
 +
 +          for (i = 0; i < n_monitors; i++)
 +            if (gdk_display_get_monitor_at_window (gdpy, gwin)
 +                == gdk_display_get_monitor (gdpy, i))
 +              break;
 +        ASET (monitor_frames, i, Fcons (frame, AREF (monitor_frames, i)));
 +      }
 +    }
 +
 +  for (i = 0; i < n_monitors; ++i)
 +    {
 +      gint width_mm, height_mm;
 +      GdkRectangle rec, work;
 +      struct MonitorInfo *mi = &monitors[i];
 +      double scale = 1;
 +
 +      GdkMonitor *monitor = gdk_display_get_monitor (gdpy, i);
 +      if (gdk_monitor_is_primary (monitor))
 +        primary_monitor = i;
 +      gdk_monitor_get_geometry (monitor, &rec);
 +
 +      width_mm = gdk_monitor_get_width_mm (monitor);
 +      height_mm = gdk_monitor_get_height_mm (monitor);
 +      gdk_monitor_get_workarea (monitor, &work);
 +
 +      /* GTK returns scaled sizes for the workareas.  */
 +      scale = pgtk_get_monitor_scale_factor (gdk_monitor_get_model (monitor));
 +      if (scale == 0.0)
 +      scale = gdk_monitor_get_scale_factor (monitor);
 +      rec.x = rec.x * scale + 0.5;
 +      rec.y = rec.y * scale + 0.5;
 +      rec.width = rec.width * scale + 0.5;
 +      rec.height = rec.height * scale + 0.5;
 +      work.x = work.x * scale + 0.5;
 +      work.y = work.y * scale + 0.5;
 +      work.width = work.width * scale + 0.5;
 +      work.height = work.height * scale + 0.5;
 +
 +      mi->geom.x = rec.x;
 +      mi->geom.y = rec.y;
 +      mi->geom.width = rec.width;
 +      mi->geom.height = rec.height;
 +      mi->work.x = work.x;
 +      mi->work.y = work.y;
 +      mi->work.width = work.width;
 +      mi->work.height = work.height;
 +      mi->mm_width = width_mm;
 +      mi->mm_height = height_mm;
 +      mi->scale_factor = scale;
 +
 +      dupstring (&mi->name, (gdk_monitor_get_model (monitor)));
 +    }
 +
 +  attributes_list = make_monitor_attribute_list (monitors,
 +                                                 n_monitors,
 +                                                 primary_monitor,
 +                                                 monitor_frames,
 +                                                 source);
 +  free_monitors (monitors, n_monitors);
 +  unblock_input ();
 +
 +  return attributes_list;
 +}
 +
 +
 +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 the currently visible tooltip.  */
 +static Lisp_Object tip_frame;
 +
 +/* The window-system window corresponding to the frame of the
 +   currently visible tooltip.  */
 +GtkWidget *tip_window;
 +
 +/* A timer that hides or deletes the currently visible tooltip when it
 +   fires.  */
 +static Lisp_Object tip_timer;
 +
 +/* STRING argument of last `x-show-tip' call.  */
 +static Lisp_Object tip_last_string;
 +
 +/* Normalized FRAME argument of last `x-show-tip' call.  */
 +static Lisp_Object tip_last_frame;
 +
 +/* PARMS argument of last `x-show-tip' call.  */
 +static Lisp_Object tip_last_parms;
 +
 +
 +static void
 +unwind_create_tip_frame (Lisp_Object frame)
 +{
 +  Lisp_Object deleted;
 +
 +  deleted = unwind_create_frame (frame);
 +  if (EQ (deleted, Qt))
 +    {
 +      tip_window = NULL;
 +      tip_frame = Qnil;
 +    }
 +}
 +
 +
 +/* Create a frame for a tooltip on the display described by DPYINFO.
 +   PARMS is a list of frame parameters.  TEXT is the string to
 +   display in the tip frame.  Value is the frame.
 +
 +   Note that functions called here, esp. gui_default_parameter can
 +   signal errors, for instance when a specified color name is
 +   undefined.  We have to make sure that we're in a consistent state
 +   when this happens.  */
 +
 +static Lisp_Object
 +x_create_tip_frame (struct pgtk_display_info *dpyinfo, Lisp_Object parms, struct frame *p)
 +{
 +  struct frame *f;
 +  Lisp_Object frame;
 +  Lisp_Object name;
-   int x_width = 0, x_height = 0;
 +  ptrdiff_t count = SPECPDL_INDEX ();
 +  bool face_change_before = face_change;
-   gui_figure_window_size (f, parms, false, false, &x_width, &x_height);
 +
 +  if (!dpyinfo->terminal->name)
 +    error ("Terminal is not live, can't create new frames on it");
 +
 +  parms = Fcopy_alist (parms);
 +
 +  /* Get the name of the frame to use for resource lookup.  */
 +  name = gui_display_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");
 +
 +  frame = Qnil;
 +  f = make_frame (false);
 +  f->wants_modeline = false;
 +  XSETFRAME (frame, f);
 +  record_unwind_protect (unwind_create_tip_frame, frame);
 +
 +  f->terminal = dpyinfo->terminal;
 +
 +  /* By setting the output method, we're essentially saying that
 +     the frame is live, as per FRAME_LIVE_P.  If we get a signal
 +     from this point on, x_destroy_window might screw up reference
 +     counts etc.  */
 +  f->output_method = output_pgtk;
 +  f->output_data.pgtk = xzalloc (sizeof *f->output_data.pgtk);
 +#if 0
 +  f->output_data.pgtk->icon_bitmap = -1;
 +#endif
 +  FRAME_FONTSET (f) = -1;
 +  f->output_data.pgtk->white_relief.pixel = -1;
 +  f->output_data.pgtk->black_relief.pixel = -1;
 +
 +  f->tooltip = true;
 +  fset_icon_name (f, Qnil);
 +  FRAME_DISPLAY_INFO (f) = dpyinfo;
 +  f->output_data.pgtk->parent_desc = FRAME_DISPLAY_INFO (f)->root_window;
 +  f->output_data.pgtk->explicit_parent = false;
 +
 +  /* 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;
 +    f->output_data.pgtk->border_pixel = -1;
 +
 +    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));
 +    f->output_data.pgtk->border_pixel
 +      = x_decode_color (f, black, BLACK_PIX_DEFAULT (f));
 +  }
 +
 +  /* 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);
 +#ifdef HAVE_HARFBUZZ
 +  register_font_driver (&ftcrhbfont_driver, f);
 +#endif        /* HAVE_HARFBUZZ */
 +
 +  image_cache_refcount =
 +    FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0;
 +
 +  gui_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.  */
 +  pgtk_default_font_parameter (f, parms);
 +
 +  gui_default_parameter (f, parms, Qborder_width, make_fixnum (0),
 +                         "borderWidth", "BorderWidth", RES_TYPE_NUMBER);
 +
 +  /* This defaults to 2 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 = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width,
 +                                   "internalBorder", "internalBorder",
 +                                   RES_TYPE_NUMBER);
 +      if (! EQ (value, Qunbound))
 +      parms = Fcons (Fcons (Qinternal_border_width, value),
 +                     parms);
 +    }
 +
 +  gui_default_parameter (f, parms, Qinternal_border_width, make_fixnum (1),
 +                         "internalBorderWidth", "internalBorderWidth",
 +                         RES_TYPE_NUMBER);
 +  gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0),
 +                         NULL, NULL, RES_TYPE_NUMBER);
 +  gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
 +                         NULL, NULL, RES_TYPE_NUMBER);
 +
 +  /* Also do the stuff which must be set before the window exists.  */
 +  gui_default_parameter (f, parms, Qforeground_color, build_string ("black"),
 +                         "foreground", "Foreground", RES_TYPE_STRING);
 +  gui_default_parameter (f, parms, Qbackground_color, build_string ("white"),
 +                         "background", "Background", RES_TYPE_STRING);
 +  gui_default_parameter (f, parms, Qmouse_color, build_string ("black"),
 +                         "pointerColor", "Foreground", RES_TYPE_STRING);
 +  gui_default_parameter (f, parms, Qcursor_color, build_string ("black"),
 +                         "cursorColor", "Foreground", RES_TYPE_STRING);
 +  gui_default_parameter (f, parms, Qborder_color, build_string ("black"),
 +                         "borderColor", "BorderColor", RES_TYPE_STRING);
 +  gui_default_parameter (f, parms, Qno_special_glyphs, Qnil,
 +                         NULL, NULL, RES_TYPE_BOOLEAN);
 +
 +  /* Init faces before gui_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);
 +
 +  f->output_data.pgtk->parent_desc = FRAME_DISPLAY_INFO (f)->root_window;
 +
 +  gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil,
 +                         "inhibitDoubleBuffering", "InhibitDoubleBuffering",
 +                         RES_TYPE_BOOLEAN);
 +
-   /* Dimensions, especially FRAME_LINES (f), must be done via change_frame_size.
-      Change will not be effected unless different from the current
-      FRAME_LINES (f).  */
-   width = FRAME_COLS (f);
-   height = FRAME_LINES (f);
-   SET_FRAME_COLS (f, 0);
-   SET_FRAME_LINES (f, 0);
-   change_frame_size (f, width, height, true, false, false, false);
++  gui_figure_window_size (f, parms, false, false);
 +
 +  xg_create_frame_widgets (f);
 +  pgtk_set_event_handler (f);
 +  tip_window = FRAME_GTK_OUTER_WIDGET (f);
 +  gtk_window_set_transient_for (GTK_WINDOW (tip_window),
 +                              GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (p)));
 +  gtk_window_set_attached_to (GTK_WINDOW (tip_window), FRAME_GTK_WIDGET (p));
 +  gtk_window_set_destroy_with_parent (GTK_WINDOW (tip_window), TRUE);
 +  gtk_window_set_decorated (GTK_WINDOW (tip_window), FALSE);
 +  gtk_window_set_type_hint (GTK_WINDOW (tip_window), GDK_WINDOW_TYPE_HINT_TOOLTIP);
 +  f->output_data.pgtk->current_cursor = f->output_data.pgtk->text_cursor;
 +  gtk_widget_show_all (FRAME_GTK_OUTER_WIDGET (f));
 +  gdk_window_set_cursor (gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f)),
 +                       f->output_data.pgtk->current_cursor);
 +
 +#if 0
 +  x_make_gc (f);
 +#endif
 +
 +  gui_default_parameter (f, parms, Qauto_raise, Qnil,
 +                         "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN);
 +  gui_default_parameter (f, parms, Qauto_lower, Qnil,
 +                         "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN);
 +  gui_default_parameter (f, parms, Qcursor_type, Qbox,
 +                         "cursorType", "CursorType", RES_TYPE_SYMBOL);
 +  gui_default_parameter (f, parms, Qalpha, Qnil,
 +                         "alpha", "Alpha", RES_TYPE_NUMBER);
 +
 +  /* Add `tooltip' frame parameter's default value. */
 +  if (NILP (Fframe_parameter (frame, Qtooltip)))
 +    {
 +      AUTO_FRAME_ARG (arg, Qtooltip, Qt);
 +      Fmodify_frame_parameters (frame, arg);
 +    }
 +
 +  /* FIXME - can this be done in a similar way to normal frames?
 +     https://lists.gnu.org/r/emacs-devel/2007-10/msg00641.html */
 +
 +  /* Set the `display-type' frame parameter before setting up faces. */
 +  {
 +    Lisp_Object disptype;
 +
 +    disptype = intern ("color");
 +
 +    if (NILP (Fframe_parameter (frame, Qdisplay_type)))
 +      {
 +      AUTO_FRAME_ARG (arg, Qdisplay_type, disptype);
 +      Fmodify_frame_parameters (frame, arg);
 +      }
 +  }
 +
 +  /* Set up faces after all frame parameters are known.  This call
 +     also merges in face attributes specified for new frames.
 +
 +     Frame parameters may be changed if .Xdefaults contains
 +     specifications for the default font.  For example, if there is an
 +     `Emacs.default.attributeBackground: pink', the `background-color'
 +     attribute of the frame get's set, which let's the internal border
 +     of the tooltip frame appear in pink.  Prevent this.  */
 +  {
 +    Lisp_Object bg = Fframe_parameter (frame, Qbackground_color);
 +
 +    call2 (Qface_set_after_frame_default, frame, Qnil);
 +
 +    if (!EQ (bg, Fframe_parameter (frame, Qbackground_color)))
 +      {
 +      AUTO_FRAME_ARG (arg, Qbackground_color, bg);
 +      Fmodify_frame_parameters (frame, arg);
 +      }
 +  }
 +
 +  f->no_split = true;
 +
 +  /* Now that the frame will be official, it counts as a reference to
 +     its display and terminal.  */
 +  FRAME_DISPLAY_INFO (f)->reference_count++;
 +  f->terminal->reference_count++;
 +
 +  /* It is now ok to make the frame official even if we get an error
 +     below.  And the frame needs to be on Vframe_list or making it
 +     visible won't work.  */
 +  Vframe_list = Fcons (frame, Vframe_list);
 +  f->can_set_window_size = true;
++  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
++                   0, true, Qtip_frame);
 +
 +  /* Setting attributes of faces of the tooltip frame from resources
 +     and similar will set face_change, which leads to the clearing of
 +     all current matrices.  Since this isn't necessary here, avoid it
 +     by resetting face_change to the value it had before we created
 +     the tip frame.  */
 +  face_change = face_change_before;
 +
 +  /* Discard the unwind_protect.  */
 +  return unbind_to (count, frame);
 +}
 +
 +/* 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;
 +    }
 +
 +  /* Any GTK+ system tooltip can be found via the x_output structure of
 +     tip_last_frame, provided that frame is still live.  Any Emacs
 +     tooltip is found via the tip_frame variable.  Note that the current
 +     value of x_gtk_use_system_tooltips might not be the same as used
 +     for the tooltip we have to hide, see Bug#30399.  */
 +  if ((NILP (tip_last_frame) && NILP (tip_frame))
 +      || (!x_gtk_use_system_tooltips
 +        && !delete
 +        && FRAMEP (tip_frame)
 +        && FRAME_LIVE_P (XFRAME (tip_frame))
 +        && !FRAME_VISIBLE_P (XFRAME (tip_frame))))
 +    /* Either there's no tooltip to hide or it's an already invisible
 +       Emacs tooltip and we don't want to change its type.  Return
 +       quickly.  */
 +    return Qnil;
 +  else
 +    {
 +      ptrdiff_t count;
 +      Lisp_Object was_open = Qnil;
 +
 +      count = SPECPDL_INDEX ();
 +      specbind (Qinhibit_redisplay, Qt);
 +      specbind (Qinhibit_quit, Qt);
 +
 +      /* Try to hide the GTK+ system tip first.  */
 +      if (FRAMEP (tip_last_frame))
 +      {
 +        struct frame *f = XFRAME (tip_last_frame);
 +
 +        if (FRAME_LIVE_P (f))
 +          {
 +            if (xg_hide_tooltip (f))
 +              was_open = Qt;
 +          }
 +      }
 +
 +      /* When using GTK+ system tooltips (compare Bug#41200) reset
 +       tip_last_frame.  It will be reassigned when showing the next
 +       GTK+ system tooltip.  */
 +      if (x_gtk_use_system_tooltips)
 +      tip_last_frame = Qnil;
 +
 +      /* Now look whether there's an Emacs tip around.  */
 +      if (FRAMEP (tip_frame))
 +      {
 +        struct frame *f = XFRAME (tip_frame);
 +
 +        if (FRAME_LIVE_P (f))
 +          {
 +            if (delete || x_gtk_use_system_tooltips)
 +              {
 +                /* Delete the Emacs tooltip frame when DELETE is true
 +                   or we change the tooltip type from an Emacs one to
 +                   a GTK+ system one.  */
 +                delete_frame (tip_frame, Qnil);
 +                tip_frame = Qnil;
 +              }
 +            else
 +              pgtk_make_frame_invisible (f);
 +
 +            was_open = Qt;
 +          }
 +        else
 +          tip_frame = Qnil;
 +      }
 +      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, *tip_f;
 +  struct window *w;
 +  int root_x, root_y;
 +  struct buffer *old_buffer;
 +  struct text_pos pos;
 +  int width, height;
 +  int old_windows_or_buffers_changed = windows_or_buffers_changed;
 +  ptrdiff_t count = SPECPDL_INDEX ();
 +  ptrdiff_t count_1;
 +  Lisp_Object window, size, tip_buf;
 +  AUTO_STRING (tip, " *tip*");
 +
 +  specbind (Qinhibit_redisplay, Qt);
 +
 +  CHECK_STRING (string);
 +  if (SCHARS (string) == 0)
 +    string = make_unibyte_string (" ", 1);
 +
 +  if (NILP (frame))
 +    frame = selected_frame;
 +  f = decode_window_system_frame (frame);
 +
 +  if (!FRAME_GTK_OUTER_WIDGET (f))
 +    return unbind_to (count, Qnil);
 +
 +  if (NILP (timeout))
 +    timeout = make_fixnum (5);
 +  else
 +    CHECK_FIXNAT (timeout);
 +
 +  if (NILP (dx))
 +    dx = make_fixnum (5);
 +  else
 +    CHECK_FIXNUM (dx);
 +
 +  if (NILP (dy))
 +    dy = make_fixnum (-10);
 +  else
 +    CHECK_FIXNUM (dy);
 +
 +  if (x_gtk_use_system_tooltips)
 +    {
 +      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);
 +        tip_last_frame = frame;
 +        }
 +
 +      unblock_input ();
 +      if (ok) goto start_timer;
 +    }
 +
 +  if (FRAMEP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame)))
 +    {
 +      if (FRAME_VISIBLE_P (XFRAME (tip_frame))
 +        && EQ (frame, tip_last_frame)
 +        && !NILP (Fequal_including_properties (tip_last_string, string))
 +        && !NILP (Fequal (tip_last_parms, parms)))
 +      {
 +        /* Only DX and DY have changed.  */
 +        tip_f = XFRAME (tip_frame);
 +        if (!NILP (tip_timer))
 +          {
 +            call1 (Qcancel_timer, tip_timer);
 +            tip_timer = Qnil;
 +          }
 +
 +        block_input ();
 +        compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f),
 +                        FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y);
 +        gtk_window_move (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (tip_f)), root_x, root_y);
 +        unblock_input ();
 +
 +        goto start_timer;
 +      }
 +      else if (tooltip_reuse_hidden_frame && EQ (frame, tip_last_frame))
 +      {
 +        bool delete = false;
 +        Lisp_Object tail, elt, parm, last;
 +
 +        /* Check if every parameter in PARMS has the same value in
 +           tip_last_parms.  This may destruct tip_last_parms which,
 +           however, will be recreated below.  */
 +        for (tail = parms; CONSP (tail); tail = XCDR (tail))
 +          {
 +            elt = XCAR (tail);
 +            parm = Fcar (elt);
 +            /* The left, top, right and bottom parameters are handled
 +               by compute_tip_xy so they can be ignored here.  */
 +            if (!EQ (parm, Qleft) && !EQ (parm, Qtop)
 +                && !EQ (parm, Qright) && !EQ (parm, Qbottom))
 +              {
 +                last = Fassq (parm, tip_last_parms);
 +                if (NILP (Fequal (Fcdr (elt), Fcdr (last))))
 +                  {
 +                    /* We lost, delete the old tooltip.  */
 +                    delete = true;
 +                    break;
 +                  }
 +                else
 +                  tip_last_parms =
 +                    call2 (Qassq_delete_all, parm, tip_last_parms);
 +              }
 +            else
 +              tip_last_parms =
 +                call2 (Qassq_delete_all, parm, tip_last_parms);
 +          }
 +
 +        /* Now check if every parameter in what is left of
 +           tip_last_parms with a non-nil value has an association in
 +           PARMS.  */
 +        for (tail = tip_last_parms; CONSP (tail); tail = XCDR (tail))
 +          {
 +            elt = XCAR (tail);
 +            parm = Fcar (elt);
 +            if (!EQ (parm, Qleft) && !EQ (parm, Qtop) && !EQ (parm, Qright)
 +                && !EQ (parm, Qbottom) && !NILP (Fcdr (elt)))
 +              {
 +                /* We lost, delete the old tooltip.  */
 +                delete = true;
 +                break;
 +              }
 +          }
 +
 +        x_hide_tip (delete);
 +      }
 +      else
 +      x_hide_tip (true);
 +    }
 +  else
 +    x_hide_tip (true);
 +
 +  tip_last_frame = frame;
 +  tip_last_string = string;
 +  tip_last_parms = parms;
 +
 +  if (!FRAMEP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame)))
 +    {
 +      /* Add default values to frame parameters.  */
 +      if (NILP (Fassq (Qname, parms)))
 +      parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms);
 +      if (NILP (Fassq (Qinternal_border_width, parms)))
 +      parms = Fcons (Fcons (Qinternal_border_width, make_fixnum (3)), parms);
 +      if (NILP (Fassq (Qborder_width, parms)))
 +      parms = Fcons (Fcons (Qborder_width, make_fixnum (1)), parms);
 +      if (NILP (Fassq (Qborder_color, parms)))
 +      parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), parms);
 +      if (NILP (Fassq (Qbackground_color, parms)))
 +      parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")),
 +                     parms);
 +
 +      /* Create a frame for the tooltip, and record it in the global
 +       variable tip_frame.  */
 +      if (NILP (tip_frame = x_create_tip_frame (FRAME_DISPLAY_INFO (f), parms, f)))
 +      /* Creating the tip frame failed.  */
 +      return unbind_to (count, Qnil);
 +    }
 +
 +  tip_f = XFRAME (tip_frame);
 +  window = FRAME_ROOT_WINDOW (tip_f);
 +  tip_buf = Fget_buffer_create (tip, Qnil);
 +  /* We will mark the tip window a "pseudo-window" below, and such
 +     windows cannot have display margins.  */
 +  bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
 +  bset_right_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
 +  set_window_buffer (window, tip_buf, false, false);
 +  w = XWINDOW (window);
 +  w->pseudo_window_p = true;
 +
 +  /* Set up the frame's root window.  Note: The following code does not
 +     try to size the window or its frame correctly.  Its only purpose is
 +     to make the subsequent text size calculations work.  The right
 +     sizes should get installed when the toolkit gets back to us.  */
 +  w->left_col = 0;
 +  w->top_line = 0;
 +  w->pixel_left = 0;
 +  w->pixel_top = 0;
 +
 +  if (CONSP (Vx_max_tooltip_size)
 +      && RANGED_FIXNUMP (1, XCAR (Vx_max_tooltip_size), INT_MAX)
 +      && RANGED_FIXNUMP (1, XCDR (Vx_max_tooltip_size), INT_MAX))
 +    {
 +      w->total_cols = XFIXNAT (XCAR (Vx_max_tooltip_size));
 +      w->total_lines = XFIXNAT (XCDR (Vx_max_tooltip_size));
 +    }
 +  else
 +    {
 +      w->total_cols = 80;
 +      w->total_lines = 40;
 +    }
 +
 +  w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f);
 +  w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f);
 +  FRAME_TOTAL_COLS (tip_f) = w->total_cols;
 +  adjust_frame_glyphs (tip_f);
 +
 +  /* Insert STRING into root window's buffer and fit the frame to the
 +     buffer.  */
 +  count_1 = SPECPDL_INDEX ();
 +  old_buffer = current_buffer;
 +  set_buffer_internal_1 (XBUFFER (w->contents));
 +  bset_truncate_lines (current_buffer, Qnil);
 +  specbind (Qinhibit_read_only, Qt);
 +  specbind (Qinhibit_modification_hooks, Qt);
 +  specbind (Qinhibit_point_motion_hooks, Qt);
 +  Ferase_buffer ();
 +  Finsert (1, &string);
 +  clear_glyph_matrix (w->desired_matrix);
 +  clear_glyph_matrix (w->current_matrix);
 +  SET_TEXT_POS (pos, BEGV, BEGV_BYTE);
 +  try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
 +  /* Calculate size of tooltip window.  */
 +  size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil,
 +                                make_fixnum (w->pixel_height), Qnil);
 +  /* Add the frame's internal border to calculated size.  */
 +  width = XFIXNUM (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
 +  height = XFIXNUM (Fcdr (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
 +
 +  /* Calculate position of tooltip frame.  */
 +  compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y);
 +
 +  /* Show tooltip frame.  */
 +  block_input ();
 +  gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (tip_f)), width, height);
 +  gtk_window_move (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (tip_f)), root_x, root_y);
 +  unblock_input ();
 +
 +  pgtk_cr_update_surface_desired_size (tip_f, width, height);
 +
 +  w->must_be_updated_p = true;
 +  update_single_window (w);
 +  flush_frame (tip_f);
 +  set_buffer_internal_1 (old_buffer);
 +  unbind_to (count_1, Qnil);
 +  windows_or_buffers_changed = old_windows_or_buffers_changed;
 +
 + 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;
 +
 +  /* Get these here because they can't be got in configure_event(). */
 +  int left_pos, top_pos;
 +  if (FRAME_GTK_OUTER_WIDGET (f)) {
 +    gtk_window_get_position (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
 +                           &left_pos, &top_pos);
 +  } else {
 +    if (FRAME_GTK_WIDGET (f) == NULL)
 +      return Qnil;    /* This can occur while creating a frame. */
 +    GtkAllocation alloc;
 +    gtk_widget_get_allocation (FRAME_GTK_WIDGET (f), &alloc);
 +    left_pos = alloc.x;
 +    top_pos = alloc.y;
 +  }
 +
 +  int native_left = left_pos + border;
 +  int native_top = top_pos + border + title_height;
 +  int native_right = left_pos + outer_width - border;
 +  int native_bottom = top_pos + outer_height - border;
 +  int internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f);
 +  int tab_bar_height = 0, tab_bar_width = 0;
 +  int tool_bar_height = FRAME_TOOLBAR_HEIGHT (f);
 +  int tool_bar_width = (tool_bar_height
 +                      ? outer_width - 2 * internal_border_width : 0);
 +
 +  tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
 +  tab_bar_width = (tab_bar_height
 +                 ? native_width - 2 * internal_border_width : 0);
 +  // inner_top += tab_bar_height;
 +
 +  /* Construct list.  */
 +  if (EQ (attribute, Qouter_edges))
 +    return list4 (make_fixnum (left_pos), make_fixnum (top_pos),
 +                make_fixnum (left_pos + outer_width),
 +                make_fixnum (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
 +      list (Fcons (Qouter_position,
 +                 Fcons (make_fixnum (left_pos),
 +                        make_fixnum (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 (Qtab_bar_size,
 +                 Fcons (make_fixnum (tab_bar_width),
 +                        make_fixnum (tab_bar_height))),
 +          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 = gtk_widget_get_toplevel (FRAME_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 = gtk_widget_get_toplevel (FRAME_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;
 +}
 +
 +static void
 +clean_up_dialog (void)
 +{
 +  pgtk_menu_set_in_use (false);
 +}
 +
 +DEFUN ("x-file-dialog", Fx_file_dialog, Sx_file_dialog, 2, 5, 0,
 +       doc: /* 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 defined only on PGTK, NS, 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 select only directories.
 +On MS Windows 7 and later, the file selection dialog "remembers" the last
 +directory where the user selected a file, and will open that directory
 +instead of DIR on subsequent invocations of this function with the same
 +value of DIR as in previous invocations; this is standard MS Windows behavior.  */)
 +  (Lisp_Object prompt, Lisp_Object dir, Lisp_Object default_filename,
 +   Lisp_Object mustmatch, Lisp_Object only_dir_p)
 +{
 +  struct frame *f = SELECTED_FRAME ();
 +  char *fn;
 +  Lisp_Object file = Qnil;
 +  Lisp_Object decoded_file;
 +  ptrdiff_t count = SPECPDL_INDEX ();
 +  char *cdef_file;
 +
 +  check_window_system (f);
 +
 +  if (popup_activated ())
 +    error ("Trying to use a menu from within a menu-entry");
 +  else
 +    pgtk_menu_set_in_use (true);
 +
 +  CHECK_STRING (prompt);
 +  CHECK_STRING (dir);
 +
 +  /* Prevent redisplay.  */
 +  specbind (Qinhibit_redisplay, Qt);
 +  record_unwind_protect_void (clean_up_dialog);
 +
 +  block_input ();
 +
 +  if (STRINGP (default_filename))
 +    cdef_file = SSDATA (default_filename);
 +  else
 +    cdef_file = SSDATA (dir);
 +
 +  fn = xg_get_file_name (f, SSDATA (prompt), cdef_file,
 +                       !NILP (mustmatch), !NILP (only_dir_p));
 +
 +  if (fn)
 +    {
 +      file = build_string (fn);
 +      xfree (fn);
 +    }
 +
 +  unblock_input ();
 +
 +  /* Make "Cancel" equivalent to C-g.  */
 +  if (NILP (file))
 +    quit ();
 +
 +  decoded_file = DECODE_FILE (file);
 +
 +  return unbind_to (count, decoded_file);
 +}
 +
 +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");
 +  DEFSYM (Qhide, "hide");
 +  DEFSYM (Qresize_mode, "resize-mode");
 +
 +  DEFVAR_LISP ("x-cursor-fore-pixel", Vx_cursor_fore_pixel,
 +             doc: /* A string indicating the foreground color of the cursor box.  */);
 +  Vx_cursor_fore_pixel = Qnil;
 +
 +  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_restack);
 +  defsubr (&Spgtk_popup_font_panel);
 +  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 (&Sx_export_frames);
 +  defsubr (&Spgtk_page_setup_dialog);
 +  defsubr (&Spgtk_get_page_setup);
 +  defsubr (&Spgtk_print_frames_dialog);
 +  defsubr (&Spgtk_backend_display_class);
 +
 +  defsubr (&Spgtk_set_monitor_scale_factor);
 +
 +  defsubr (&Sx_file_dialog);
 +
 +  as_status = 0;
 +  as_script = Qnil;
 +  as_result = 0;
 +
 +  monitor_scale_factor_alist = Qnil;
 +  staticpro (&monitor_scale_factor_alist);
 +
 +  tip_timer = Qnil;
 +  staticpro (&tip_timer);
 +  tip_frame = Qnil;
 +  staticpro (&tip_frame);
 +  tip_last_frame = Qnil;
 +  staticpro (&tip_last_frame);
 +  tip_last_string = Qnil;
 +  staticpro (&tip_last_string);
 +  tip_last_parms = Qnil;
 +  staticpro (&tip_last_parms);
 +
 +  /* 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;
 +
 +  DEFVAR_LISP ("x-max-tooltip-size", Vx_max_tooltip_size,
 +    doc: /* Maximum size for tooltips.
 +Value is a pair (COLUMNS . ROWS).  Text larger than this is clipped.  */);
 +  Vx_max_tooltip_size = Fcons (make_fixnum (80), make_fixnum (40));
 +
 +  DEFVAR_LISP ("x-gtk-resize-child-frames", x_gtk_resize_child_frames,
 +    doc: /* If non-nil, resize child frames specially with GTK builds.
 +If this is nil, resize child frames like any other frames.  This is the
 +default and usually works with most desktops.  Some desktop environments
 +(GNOME shell in particular when using the mutter window manager),
 +however, may refuse to resize a child frame when Emacs is built with
 +GTK3.  For those environments, the two settings below are provided.
 +
 +If this equals the symbol 'hide', Emacs temporarily hides the child
 +frame during resizing.  This approach seems to work reliably, may
 +however induce some flicker when the frame is made visible again.
 +
 +If this equals the symbol 'resize-mode', Emacs uses GTK's resize mode to
 +always trigger an immediate resize of the child frame.  This method is
 +deprecated by GTK and may not work in future versions of that toolkit.
 +It also may freeze Emacs when used with other desktop environments.  It
 +avoids, however, the unpleasant flicker induced by the hiding approach.
 +
 +This variable is considered a temporary workaround and will be hopefully
 +eliminated in future versions of Emacs.  */);
 +  x_gtk_resize_child_frames = Qnil;
 +
 +
 +  DEFSYM (Qmono, "mono");
 +  DEFSYM (Qassq_delete_all, "assq-delete-all");
 +
 +  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 --cc src/pgtkterm.c
index 55c139fb7ca89a9377597a2fd379f18e7474af89,0000000000000000000000000000000000000000..3839b2b3fc421ff6cf259c58a43311db5d1b6e22
mode 100644,000000..100644
--- /dev/null
@@@ -1,7548 -1,0 +1,7557 @@@
- pgtk_set_window_size (struct frame *f,
-                     bool change_gravity,
-                     int width, int height, bool pixelwise)
 +/* Pure Gtk+-3 communication module.      -*- coding: utf-8 -*-
 +
 +Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2020 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 <cairo.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 "frame.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 "emacs-icon.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"
 +#include "emacsgtkfixed.h"
 +
 +#define STORE_KEYSYM_FOR_DEBUG(keysym) ((void)0)
 +
 +#define FRAME_CR_CONTEXT(f) ((f)->output_data.pgtk->cr_context)
 +#define FRAME_CR_ACTIVE_CONTEXT(f)    ((f)->output_data.pgtk->cr_active)
 +#define FRAME_CR_SURFACE(f) (cairo_get_target(FRAME_CR_CONTEXT(f)))
 +#define FRAME_CR_SURFACE_DESIRED_WIDTH(f)             \
 +  ((f)->output_data.pgtk->cr_surface_desired_width)
 +#define FRAME_CR_SURFACE_DESIRED_HEIGHT(f) \
 +  ((f)->output_data.pgtk->cr_surface_desired_height)
 +
 +/* Non-zero means that a HELP_EVENT has been generated since Emacs
 +   start.  */
 +
 +static bool any_help_event_p;
 +
 +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 Lisp_Object xg_default_icon_file;
 +
 +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);
 +
 +/*
 + * This is not a flip context in the same sense as gpu rendering
 + * scences, it only occurs when a new context was required due to a
 + * resize or other fundamental change.  This is called when that
 + * context's surface has completed drawing
 + */
 +
 +static void
 +flip_cr_context (struct frame *f)
 +{
 +  PGTK_TRACE ("flip_cr_context");
 +  cairo_t *cr = FRAME_CR_ACTIVE_CONTEXT (f);
 +
 +  block_input ();
 +  if (cr != FRAME_CR_CONTEXT (f))
 +    {
 +      cairo_destroy (cr);
 +      FRAME_CR_ACTIVE_CONTEXT (f) = cairo_reference (FRAME_CR_CONTEXT (f));
 +
 +    }
 +  unblock_input ();
 +}
 +
 +
 +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);
 +    }
 +}
 +
 +char *
 +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 (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);
 +  CLEAR_IF_EQ (im.focused_frame);
 +
 +#undef CLEAR_IF_EQ
 +
 +  if (f == hlinfo->mouse_face_mouse_frame)
 +    reset_mouse_highlight (hlinfo);
 +
 +  g_clear_object (&FRAME_X_OUTPUT (f)->text_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->nontext_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->modeline_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->hand_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->hourglass_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->horizontal_drag_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->vertical_drag_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->left_edge_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->right_edge_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->top_edge_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->bottom_edge_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->top_left_corner_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->top_right_corner_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->bottom_right_corner_cursor);
 +  g_clear_object (&FRAME_X_OUTPUT (f)->bottom_left_corner_cursor);
 +
 +
 +  if (FRAME_X_OUTPUT (f)->border_color_css_provider != NULL)
 +    {
 +      GtkStyleContext *ctxt = gtk_widget_get_style_context (FRAME_WIDGET (f));
 +      GtkCssProvider *old = FRAME_X_OUTPUT (f)->border_color_css_provider;
 +      gtk_style_context_remove_provider (ctxt, GTK_STYLE_PROVIDER (old));
 +      g_object_unref (old);
 +      FRAME_X_OUTPUT (f)->border_color_css_provider = NULL;
 +    }
 +
 +  if (FRAME_X_OUTPUT (f)->scrollbar_foreground_css_provider != NULL)
 +    {
 +      GtkCssProvider *old =
 +      FRAME_X_OUTPUT (f)->scrollbar_foreground_css_provider;
 +      g_object_unref (old);
 +      FRAME_X_OUTPUT (f)->scrollbar_foreground_css_provider = NULL;
 +    }
 +
 +  if (FRAME_X_OUTPUT (f)->scrollbar_background_css_provider != NULL)
 +    {
 +      GtkCssProvider *old =
 +      FRAME_X_OUTPUT (f)->scrollbar_background_css_provider;
 +      g_object_unref (old);
 +      FRAME_X_OUTPUT (f)->scrollbar_background_css_provider = NULL;
 +    }
 +
 +  gtk_widget_destroy (FRAME_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 (f->output_data.pgtk->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 (f->output_data.pgtk->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
 +   -------------------------------------------------------------------------- */
 +{
 +  PGTK_TRACE ("x_set_offset: %d,%d,%d.", xoff, yoff, change_gravity);
 +
 +  int modified_top, modified_left;
 +
 +  if (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);
 +
 +  if (x_gtk_use_window_move)
 +    {
 +      if (change_gravity != 0)
 +      {
 +        if (FRAME_GTK_OUTER_WIDGET (f))
 +          {
 +            gtk_window_move (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
 +                             f->left_pos, f->top_pos);
 +          }
 +        else
 +          {
 +            GtkWidget *fixed = FRAME_GTK_WIDGET (f);
 +            GtkWidget *parent = gtk_widget_get_parent (fixed);
 +            gtk_fixed_move (GTK_FIXED (parent), fixed,
 +                            f->left_pos, f->top_pos);
 +          }
 +      }
 +      unblock_input ();
 +      return;
 +    }
 +
 +  modified_left = f->left_pos;
 +  modified_top = f->top_pos;
 +
 +  if (FRAME_GTK_OUTER_WIDGET (f))
 +    {
 +      gtk_window_move (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
 +                     modified_left, modified_top);
 +    }
 +  else
 +    {
 +      GtkWidget *fixed = FRAME_GTK_WIDGET (f);
 +      GtkWidget *parent = gtk_widget_get_parent (fixed);
 +      gtk_fixed_move (GTK_FIXED (parent), fixed,
 +                    modified_left, modified_top);
 +    }
 +
 +  unblock_input ();
 +}
 +
 +static void
++pgtk_set_window_size (struct frame *f, bool change_gravity,
++                    int width, int height)
 +/* --------------------------------------------------------------------------
 +     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);
 +
++#if 0
 +  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);
 +    }
++#else
++  pixelwidth = width;
++  pixelheight = height;
++#endif
 +
++#if 0
 +  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))));
++#endif
 +
 +  for (GtkWidget * w = FRAME_GTK_WIDGET (f); w != NULL;
 +       w = gtk_widget_get_parent (w))
 +    {
 +      gint wd, hi;
 +      gtk_widget_get_size_request (w, &wd, &hi);
 +    }
 +
 +  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_WIDGET (f));
 +
 +  unblock_input ();
 +}
 +
 +void
 +pgtk_iconify_frame (struct frame *f)
 +/* --------------------------------------------------------------------------
 +     External: Iconify window
 +   -------------------------------------------------------------------------- */
 +{
 +  PGTK_TRACE ("pgtk_iconify_frame");
 +
 +  /* Don't keep the highlight on an invisible frame.  */
 +  if (FRAME_DISPLAY_INFO (f)->highlight_frame == f)
 +    FRAME_DISPLAY_INFO (f)->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);
 +
 +  unblock_input ();
 +}
 +
 +static gboolean
 +pgtk_make_frame_visible_wait_for_map_event_cb (GtkWidget * widget,
 +                                             GdkEventAny * event,
 +                                             gpointer user_data)
 +{
 +  int *foundptr = user_data;
 +  *foundptr = 1;
 +  return FALSE;
 +}
 +
 +static gboolean
 +pgtk_make_frame_visible_wait_for_map_event_timeout (gpointer user_data)
 +{
 +  int *timedoutptr = user_data;
 +  *timedoutptr = 1;
 +  return FALSE;
 +}
 +
 +void
 +pgtk_make_frame_visible (struct frame *f)
 +/* --------------------------------------------------------------------------
 +     External: Show the window (X11 semantics)
 +   -------------------------------------------------------------------------- */
 +{
 +  PGTK_TRACE ("pgtk_make_frame_visible");
 +
 +  GtkWidget *win = FRAME_GTK_OUTER_WIDGET (f);
 +
 +  if (!FRAME_VISIBLE_P (f))
 +    {
 +      gtk_widget_show (FRAME_WIDGET (f));
 +      if (win)
 +      gtk_window_deiconify (GTK_WINDOW (win));
 +
 +      if (FLOATP (Vpgtk_wait_for_event_timeout))
 +      {
 +        guint msec =
 +          (guint) (XFLOAT_DATA (Vpgtk_wait_for_event_timeout) * 1000);
 +        int found = 0;
 +        int timed_out = 0;
 +        gulong id =
 +          g_signal_connect (FRAME_WIDGET (f), "map-event",
 +                            G_CALLBACK
 +                            (pgtk_make_frame_visible_wait_for_map_event_cb),
 +                            &found);
 +        guint src =
 +          g_timeout_add (msec,
 +                         pgtk_make_frame_visible_wait_for_map_event_timeout,
 +                         &timed_out);
 +        while (!found && !timed_out)
 +          gtk_main_iteration ();
 +        g_signal_handler_disconnect (FRAME_WIDGET (f), id);
 +        if (!timed_out)
 +          g_source_remove (src);
 +      }
 +    }
 +}
 +
 +
 +void
 +pgtk_make_frame_invisible (struct frame *f)
 +/* --------------------------------------------------------------------------
 +     External: Hide the window (X11 semantics)
 +   -------------------------------------------------------------------------- */
 +{
 +  PGTK_TRACE ("pgtk_make_frame_invisible");
 +
 +  gtk_widget_hide (FRAME_WIDGET (f));
 +
 +  SET_FRAME_VISIBLE (f, 0);
 +  SET_FRAME_ICONIFIED (f, false);
 +}
 +
 +static void
 +pgtk_make_frame_visible_invisible (struct frame *f, bool visible)
 +{
 +  if (visible)
 +    pgtk_make_frame_visible (f);
 +  else
 +    pgtk_make_frame_invisible (f);
 +}
 +
 +static Lisp_Object
 +pgtk_new_font (struct frame *f, Lisp_Object font_object, int fontset)
 +{
 +  PGTK_TRACE ("pgtk_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;
 +
++  /* We could use a more elaborate calculation here.  */
++  FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
++
 +  /* 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_parent_frame (struct frame *f, Lisp_Object new_value,
 +                  Lisp_Object old_value)
 +/* --------------------------------------------------------------------------
 +     Set frame F's `parent-frame' parameter.  If non-nil, make F a child
 +     frame of the frame specified by that parameter.  Technically, this
 +     makes F's window-system window a child window of the parent frame's
 +     window-system window.  If nil, make F's window-system window a
 +     top-level window--a child of its display's root window.
 +
 +     A child frame's `left' and `top' parameters specify positions
 +     relative to the top-left corner of its parent frame's native
 +     rectangle.  On macOS moving a parent frame moves all its child
 +     frames too, keeping their position relative to the parent
 +     unaltered.  When a parent frame is iconified or made invisible, its
 +     child frames are made invisible.  When a parent frame is deleted,
 +     its child frames are deleted too.
 +
 +     Whether a child frame has a tool bar may be window-system or window
 +     manager dependent.  It's advisable to disable it via the frame
 +     parameter settings.
 +
 +     Some window managers may not honor this parameter.
 +   -------------------------------------------------------------------------- */
 +{
 +  struct frame *p = NULL;
 +
 +  if (!NILP (new_value)
 +      && (!FRAMEP (new_value)
 +        || !FRAME_LIVE_P (p = XFRAME (new_value))
 +        || !FRAME_PGTK_P (p)))
 +    {
 +      store_frame_param (f, Qparent_frame, old_value);
 +      error ("Invalid specification of `parent-frame'");
 +    }
 +
 +  if (p != FRAME_PARENT_FRAME (f))
 +    {
 +      block_input ();
 +
 +      if (p != NULL)
 +      {
 +        if (FRAME_DISPLAY_INFO (f) != FRAME_DISPLAY_INFO (p))
 +          error ("Cross display reparent.");
 +      }
 +
 +      GtkWidget *fixed = FRAME_GTK_WIDGET (f);
 +
 +      GtkAllocation alloc;
 +      gtk_widget_get_allocation(fixed, &alloc);
 +      g_object_ref (fixed);
 +
 +      GtkCssProvider *provider = FRAME_X_OUTPUT (f)->border_color_css_provider;
 +
 +      {
 +      GtkWidget *whbox_of_f = gtk_widget_get_parent (fixed);
 +      gtk_container_remove (GTK_CONTAINER (whbox_of_f), fixed);
 +
 +      GtkStyleContext *ctxt = gtk_widget_get_style_context (FRAME_WIDGET (f));
 +      gtk_style_context_remove_provider (ctxt, GTK_STYLE_PROVIDER (provider));
 +
 +      if (FRAME_GTK_OUTER_WIDGET (f))
 +        {
 +          gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
 +          FRAME_GTK_OUTER_WIDGET (f) = NULL;
 +          FRAME_OUTPUT_DATA (f)->vbox_widget = NULL;
 +          FRAME_OUTPUT_DATA (f)->hbox_widget = NULL;
 +          FRAME_OUTPUT_DATA (f)->menubar_widget = NULL;
 +          FRAME_OUTPUT_DATA (f)->toolbar_widget = NULL;
 +          FRAME_OUTPUT_DATA (f)->ttip_widget = NULL;
 +          FRAME_OUTPUT_DATA (f)->ttip_lbl = NULL;
 +          FRAME_OUTPUT_DATA (f)->ttip_window = NULL;
 +        }
 +      }
 +
 +      if (p == NULL)
 +      {
 +        xg_create_frame_outer_widgets (f);
 +        pgtk_set_event_handler (f);
 +        gtk_box_pack_start (GTK_BOX (f->output_data.pgtk->hbox_widget), fixed, TRUE, TRUE, 0);
 +        f->output_data.pgtk->preferred_width = alloc.width;
 +        f->output_data.pgtk->preferred_height = alloc.height;
 +        x_wm_set_size_hint (f, 0, 0);
 +        xg_frame_set_char_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, alloc.width),
 +                                FRAME_PIXEL_TO_TEXT_HEIGHT (f, alloc.height));
 +        gtk_widget_queue_resize (FRAME_WIDGET (f));
 +        gtk_widget_show_all (FRAME_GTK_OUTER_WIDGET (f));
 +      }
 +      else
 +      {
 +        GtkWidget *fixed_of_p = FRAME_GTK_WIDGET (p);
 +        gtk_fixed_put (GTK_FIXED (fixed_of_p), fixed, f->left_pos, f->top_pos);
 +        gtk_widget_set_size_request (fixed, alloc.width, alloc.height);
 +        gtk_widget_show_all (fixed);
 +      }
 +
 +      GtkStyleContext *ctxt = gtk_widget_get_style_context (FRAME_WIDGET (f));
 +      gtk_style_context_add_provider (ctxt, GTK_STYLE_PROVIDER (provider),
 +                                    GTK_STYLE_PROVIDER_PRIORITY_USER);
 +
 +      g_object_unref (fixed);
 +
 +      if (FRAME_GTK_OUTER_WIDGET (f)) {
 +      if (EQ (x_gtk_resize_child_frames, Qresize_mode))
 +        gtk_container_set_resize_mode
 +          (GTK_CONTAINER (FRAME_GTK_OUTER_WIDGET (f)),
 +           p ? GTK_RESIZE_IMMEDIATE : GTK_RESIZE_QUEUE);
 +      }
 +
 +      unblock_input ();
 +
 +      fset_parent_frame (f, new_value);
 +    }
 +}
 +
 +
 +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 (!FRAME_GTK_OUTER_WIDGET (f))
 +    return;
 +
 +  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 = 96;
 +  dpyinfo->resy = 96;
 +  dpyinfo->color_p = 1;
 +  dpyinfo->n_planes = 32;
 +  dpyinfo->root_window = 42;  /* a placeholder.. */
 +  dpyinfo->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.  */
 +      Emacs_GC 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 (xgcv.foreground == xgcv.background)
 +      xgcv.foreground = FRAME_X_OUTPUT (s->f)->cursor_foreground_color;
 +      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);
 +
 +      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.  */
 +      Emacs_GC 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);
 +}
 +
 +
 +static void
 +fill_background_by_face (struct frame *f, struct face *face, int x, int y,
 +                       int width, int height)
 +{
 +  cairo_t *cr = pgtk_begin_cr_clip (f);
 +
 +  cairo_rectangle (cr, x, y, width, height);
 +  cairo_clip (cr);
 +
 +  double r = ((face->background >> 16) & 0xff) / 255.0;
 +  double g = ((face->background >> 8) & 0xff) / 255.0;
 +  double b = ((face->background >> 0) & 0xff) / 255.0;
 +  cairo_set_source_rgb (cr, r, g, b);
 +  cairo_paint (cr);
 +
 +  if (face->stipple != 0)
 +    {
 +      cairo_pattern_t *mask =
 +      FRAME_DISPLAY_INFO (f)->bitmaps[face->stipple - 1].pattern;
 +
 +      double r = ((face->foreground >> 16) & 0xff) / 255.0;
 +      double g = ((face->foreground >> 8) & 0xff) / 255.0;
 +      double b = ((face->foreground >> 0) & 0xff) / 255.0;
 +      cairo_set_source_rgb (cr, r, g, b);
 +      cairo_mask (cr, mask);
 +    }
 +
 +  pgtk_end_cr_clip (f);
 +}
 +
 +static void
 +fill_background (struct glyph_string *s, int x, int y, int width, int height)
 +{
 +  fill_background_by_face (s->f, s->face, x, y, width, height);
 +}
 +
 +/* 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_horizontal_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 (s->stippled_p)
 +      {
 +        /* Fill background with a stipple pattern.  */
 +
 +        fill_background (s,
 +                         s->x, s->y + box_line_width,
 +                         s->background_width,
 +                         s->height - 2 * box_line_width);
 +        s->background_filled_p = true;
 +      }
 +      else 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 + max (s->face->box_vertical_line_width, 0);
 +  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 + max (s->face->box_vertical_line_width, 0);
 +  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 + max (s->face->box_vertical_line_width, 0);
 +  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)
 +{
 +  Emacs_GC 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 hwidth, int vwidth, 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,
 +                         vwidth, 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 - vwidth, top_y,
 +                         vwidth, 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, hwidth);
 +      else
 +      x_fill_trapezoid_for_relief (f, top_left_color, left_x, top_y,
 +                                   right_x + 1 - left_x, hwidth, 1);
 +    }
 +  if (bot_p)
 +    {
 +      if (!left_p)
 +      pgtk_fill_rectangle (f, bottom_right_color, left_x,
 +                           bottom_y + 1 - hwidth, right_x + 1 - left_x,
 +                           hwidth);
 +      else
 +      x_fill_trapezoid_for_relief (f, bottom_right_color,
 +                                   left_x, bottom_y + 1 - hwidth,
 +                                   right_x + 1 - left_x, hwidth, 0);
 +    }
 +  if (left_p && vwidth > 1)
 +    pgtk_fill_rectangle (f, bottom_right_color, left_x, top_y,
 +                       1, bottom_y + 1 - top_y);
 +  if (top_p && hwidth > 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 hwidth,
 +               int vwidth, 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, hwidth);
 +
 +  /* Left.  */
 +  if (left_p)
 +    pgtk_fill_rectangle (s->f, s->xgcv.foreground,
 +                       left_x, top_y, vwidth, bottom_y - top_y + 1);
 +
 +  /* Bottom.  */
 +  pgtk_fill_rectangle (s->f, s->xgcv.foreground,
 +                     left_x, bottom_y - hwidth + 1, right_x - left_x + 1,
 +                     hwidth);
 +
 +  /* Right.  */
 +  if (right_p)
 +    pgtk_fill_rectangle (s->f, s->xgcv.foreground,
 +                       right_x - vwidth + 1, top_y, vwidth,
 +                       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 hwidth, vwidth, 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);
 +
 +  vwidth = eabs (s->face->box_vertical_line_width);
 +  hwidth = eabs (s->face->box_horizontal_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, hwidth,
 +                   vwidth, 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, hwidth,
 +                        vwidth, 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 += max (s->face->box_vertical_line_width, 0);
 +
 +  /* 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 = (tab_bar_button_relief < 0
 +             ? DEFAULT_TAB_BAR_BUTTON_RELIEF
 +             : (tool_bar_button_relief < 0
 +                ? DEFAULT_TOOL_BAR_BUTTON_RELIEF
 +                : min (tool_bar_button_relief, 1000000)));
 +      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 == TAB_BAR_FACE_ID)
 +    {
 +      if (CONSP (Vtab_bar_button_margin)
 +        && FIXNUMP (XCAR (Vtab_bar_button_margin))
 +        && FIXNUMP (XCDR (Vtab_bar_button_margin)))
 +      {
 +        extra_x = XFIXNUM (XCAR (Vtab_bar_button_margin));
 +        extra_y = XFIXNUM (XCDR (Vtab_bar_button_margin));
 +      }
 +      else if (FIXNUMP (Vtab_bar_button_margin))
 +      extra_x = extra_y = XFIXNUM (Vtab_bar_button_margin);
 +    }
 +
 +  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, 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 (s->stippled_p)
 +    {
 +      /* Fill background with a stipple pattern.  */
 +
 +      fill_background (s, x, y, w, h);
 +    }
 +  else
 +    x_clear_glyph_string_rect (s, x, y, w, h);
 +}
 +
 +static void
 +x_cr_draw_image (struct frame *f, Emacs_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 = 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_translate (cr, dest_x - src_x, dest_y - src_y);
 +
 +  cairo_surface_t *surface;
 +  cairo_pattern_get_surface (image, &surface);
 +  cairo_format_t format = cairo_image_surface_get_format (surface);
 +  if (format != CAIRO_FORMAT_A8 && format != CAIRO_FORMAT_A1)
 +    {
 +      cairo_set_source (cr, image);
 +      cairo_fill (cr);
 +    }
 +  else
 +    {
 +      pgtk_set_cr_source_with_gc_foreground (f, gc);
 +      cairo_clip (cr);
 +      cairo_mask (cr, image);
 +    }
 +
 +  pgtk_end_cr_clip (f);
 +}
 +
 +/* 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 += max (s->face->box_vertical_line_width, 0);
 +
 +  /* 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->img->cr_data)
 +    {
 +      cairo_t *cr = pgtk_begin_cr_clip (s->f);
 +      x_set_glyph_string_clipping (s, cr);
 +      x_cr_draw_image (s->f, &s->xgcv, s->img->cr_data,
 +                     s->slice.x, s->slice.y, s->slice.width, s->slice.height,
 +                     x, y, true);
 +      if (!s->img->mask)
 +      {
 +        /* When the image has a mask, we can expect that at
 +           least part of a mouse highlight or a block cursor will
 +           be visible.  If the image doesn't have a mask, make
 +           a block cursor visible by drawing a rectangle around
 +           the image.  I believe it's looking better if we do
 +           nothing here for mouse-face.  */
 +        if (s->hl == DRAW_CURSOR)
 +          {
 +            int relief = eabs (s->img->relief);
 +            pgtk_draw_rectangle (s->f, s->xgcv.foreground, x - relief, y - relief,
 +                                 s->slice.width + relief*2 - 1,
 +                                 s->slice.height + relief*2 - 1);
 +          }
 +      }
 +      pgtk_end_cr_clip (s->f);
 +    }
 +  else
 +    /* 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 = max (s->face->box_vertical_line_width, 0);
 +  int box_line_vwidth = max (s->face->box_horizontal_line_width, 0);
 +  int height;
 +
 +  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->width != s->background_width)
 +    {
 +      {
 +        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.  */
 +  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);
 +}
 +
 +/* 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 (s->face->stipple)
 +          {
 +            /* Fill background with a stipple pattern.  */
 +            fill_background (s, x, y, w, h);
 +          }
 +        else
 +          {
 +            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, text_left_x = window_box_left_offset (s->w, TEXT_AREA);
 +
 +      /* Don't draw into left fringe or scrollbar area except for
 +         header line and mode line.  */
 +      if (x < text_left_x && !s->row->mode_line_p)
 +      {
 +        int left_x = WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH (s->w);
 +        int right_x = text_left_x;
 +
 +        if (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (s->w))
 +          left_x += WINDOW_LEFT_FRINGE_WIDTH (s->w);
 +        else
 +          right_x -= WINDOW_LEFT_FRINGE_WIDTH (s->w);
 +
 +        /* Adjust X and BACKGROUND_WIDTH to fit inside the space
 +           between LEFT_X and RIGHT_X.  */
 +        if (x < left_x)
 +          {
 +            background_width -= left_x - x;
 +            x = left_x;
 +          }
 +        if (x + background_width > right_x)
 +          background_width = right_x - 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)
 +{
 +  struct frame *f = XFRAME (w->frame);
 +  PGTK_TRACE ("draw_window_cursor: %d, %d, %d, %d, %d, %d.",
 +            x, y, cursor_type, cursor_width, on_p, active_p);
 +  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 ();
 +          }
 +      }
 +
 +      if (w == XWINDOW (f->selected_window))
 +      {
 +        int frame_x =
 +          WINDOW_TO_FRAME_PIXEL_X (w, x) + WINDOW_LEFT_FRINGE_WIDTH (w);
 +        int frame_y = WINDOW_TO_FRAME_PIXEL_Y (w, y);
 +        pgtk_im_set_cursor_location (f, frame_x, frame_y,
 +                                     w->phys_cursor_width,
 +                                     w->phys_cursor_height);
 +      }
 +    }
 +
 +}
 +
 +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.  */
 +  gui_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 ();
 +}
 +
 +/* Icons.  */
 +
 +/* Make the x-window of frame F use the gnu icon bitmap.  */
 +
 +static bool
 +pgtk_bitmap_icon (struct frame *f, Lisp_Object file)
 +{
 +  ptrdiff_t bitmap_id;
 +
 +  if (FRAME_GTK_WIDGET (f) == 0)
 +    return true;
 +
 +  /* Free up our existing icon bitmap and mask if any.  */
 +  if (f->output_data.pgtk->icon_bitmap > 0)
 +    image_destroy_bitmap (f, f->output_data.pgtk->icon_bitmap);
 +  f->output_data.pgtk->icon_bitmap = 0;
 +
 +  if (STRINGP (file))
 +    {
 +      /* Use gtk_window_set_icon_from_file () if available,
 +       It's not restricted to bitmaps */
 +      if (xg_set_icon (f, file))
 +      return false;
 +      bitmap_id = image_create_bitmap_from_file (f, file);
 +    }
 +  else
 +    {
 +      /* Create the GNU bitmap and mask if necessary.  */
 +      if (FRAME_DISPLAY_INFO (f)->icon_bitmap_id < 0)
 +      {
 +        ptrdiff_t rc = -1;
 +
 +          if (xg_set_icon (f, xg_default_icon_file)
 +              || xg_set_icon_from_xpm_data (f, gnu_xpm_bits))
 +            {
 +              FRAME_DISPLAY_INFO (f)->icon_bitmap_id = -2;
 +              return false;
 +            }
 +
 +        /* If all else fails, use the (black and white) xbm image. */
 +        if (rc == -1)
 +          {
 +              rc = image_create_bitmap_from_data (f,
 +                                                  (char *) gnu_xbm_bits,
 +                                                  gnu_xbm_width,
 +                                                  gnu_xbm_height);
 +            if (rc == -1)
 +              return true;
 +
 +            FRAME_DISPLAY_INFO (f)->icon_bitmap_id = rc;
 +          }
 +      }
 +
 +      /* The first time we create the GNU bitmap and mask,
 +       this increments the ref-count one extra time.
 +       As a result, the GNU bitmap and mask are never freed.
 +       That way, we don't have to worry about allocating it again.  */
 +      image_reference_bitmap (f, FRAME_DISPLAY_INFO (f)->icon_bitmap_id);
 +
 +      bitmap_id = FRAME_DISPLAY_INFO (f)->icon_bitmap_id;
 +    }
 +
 +  if (FRAME_DISPLAY_INFO (f)->bitmaps[bitmap_id - 1].img != NULL)
 +    {
 +      gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
 +                         FRAME_DISPLAY_INFO (f)->bitmaps[bitmap_id - 1].img);
 +    }
 +  f->output_data.pgtk->icon_bitmap = bitmap_id;
 +
 +  return false;
 +}
 +
 +
 +/* Make the x-window of frame F use a rectangle with text.
 +   Use ICON_NAME as the text.  */
 +
 +bool
 +pgtk_text_icon (struct frame *f, const char *icon_name)
 +{
 +  if (FRAME_GTK_OUTER_WIDGET (f)) {
 +    gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), NULL);
 +    gtk_window_set_title (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), icon_name);
 +  }
 +
 +  return false;
 +}
 +
 +/***********************************************************************
 +                  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)
 +{
 +  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))
 +          gui_draw_right_divider (w);
 +        else
 +          gui_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)
 +{
 +  GtkWidget *widget = FRAME_GTK_WIDGET (f);
 +  /* Mouse highlight may be displayed again.  */
 +  MOUSE_HL_INFO (f)->mouse_face_defer = false;
 +
 +  gtk_widget_queue_draw (widget);
 +  flip_cr_context (f);
 +}
 +
 +/* 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;
 +  GdkModifierType mask;
 +  GdkWindow *win;
 +
 +  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;
 +
 +  if (gui_mouse_grabbed (dpyinfo))
 +    {
 +      /* 1.1. use last_mouse_frame as frame where the pointer is on. */
 +      f1 = dpyinfo->last_mouse_frame;
 +    }
 +  else
 +    {
 +      f1 = *fp;
 +      /* 1.2. get frame where the pointer is on. */
 +      win = gtk_widget_get_window (FRAME_GTK_WIDGET (*fp));
 +      seat = gdk_display_get_default_seat (dpyinfo->gdpy);
 +      device = gdk_seat_get_pointer (seat);
 +      win =
 +      gdk_window_get_device_position (win, device, &win_x, &win_y, &mask);
 +      if (win != NULL)
 +      f1 = pgtk_any_window_to_frame (win);
 +      else
 +      {
 +        // crossing display server?
 +        f1 = SELECTED_FRAME ();
 +      }
 +    }
 +
 +  /* 2. get the display and the device. */
 +  win = gtk_widget_get_window (FRAME_GTK_WIDGET (f1));
 +  GdkDisplay *gdpy = gdk_window_get_display (win);
 +  seat = gdk_display_get_default_seat (gdpy);
 +  device = gdk_seat_get_pointer (seat);
 +
 +  /* 3. get x, y relative to edit window of the frame. */
 +  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, Emacs_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 = pgtk_begin_cr_clip (f);
 +
 +  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);
 +
 +  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_translate (cr, dest_x - src_x, dest_y - src_y);
 +
 +  cairo_surface_t *surface;
 +  cairo_pattern_get_surface (image, &surface);
 +  cairo_format_t format = cairo_image_surface_get_format (surface);
 +  if (format != CAIRO_FORMAT_A8 && format != CAIRO_FORMAT_A1)
 +    {
 +      cairo_set_source (cr, image);
 +      cairo_fill (cr);
 +    }
 +  else
 +    {
 +      pgtk_set_cr_source_with_gc_foreground (f, gc);
 +      cairo_clip (cr);
 +      cairo_mask (cr, image);
 +    }
 +
 +  pgtk_end_cr_clip (f);
 +}
 +
 +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 (face->stipple)
 +      {
 +        fill_background_by_face (f, face, p->bx, p->by, p->nx, p->ny);
 +      }
 +      else
 +      {
 +        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)
 +    {
 +      Emacs_GC 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)
 +{
 +}
 +
 +extern frame_parm_handler pgtk_frame_parm_handlers[];
 +
 +static struct redisplay_interface pgtk_redisplay_interface = {
 +  pgtk_frame_parm_handlers,
 +  gui_produce_glyphs,
 +  gui_write_glyphs,
 +  gui_insert_glyphs,
 +  gui_clear_end_of_line,
 +  pgtk_scroll_run,
 +  pgtk_after_update_window_line,
 +  pgtk_update_window_begin,
 +  pgtk_update_window_end,
 +  pgtk_flush_display,
 +  gui_clear_window_mouse_face,
 +  gui_get_glyph_overhangs,
 +  gui_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_clear_under_internal_border,
 +  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,
 +  pgtk_default_font_parameter,
 +};
 +
 +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;
 +}
 +
 +static void
 +pgtk_flash (struct frame *f)
 +{
 +  block_input ();
 +
 +  {
 +    cairo_surface_t *surface_orig = FRAME_CR_SURFACE (f);
 +
 +    int width = FRAME_CR_SURFACE_DESIRED_WIDTH (f);
 +    int height = FRAME_CR_SURFACE_DESIRED_HEIGHT (f);
 +    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;
 +      {
 +      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
 +    }
 +
 +    cairo_destroy (cr);
 +  }
 +
 +  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.  */
 +
 +  /* Before sleep, dispatch draw events.
 +   * Don't do this after g_main_context_query(), because fd may be closed
 +   * in dispatch.
 +   */
 +  if (context_acquired)
 +    {
 +      int pselect_errno = errno;
 +      block_input ();
 +      while (g_main_context_pending (context))
 +      g_main_context_dispatch (context);
 +      unblock_input ();
 +      errno = pselect_errno;
 +    }
 +
 +  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)
 +{
 +  if (!FRAME_GTK_OUTER_WIDGET (f))
 +    return;
 +
 +  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 ();
 +    }
 +}
 +
 +/* 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 ();
 +
 +  pgtk_im_finish (dpyinfo);
 +
 +  /* Normally, the display is available...  */
 +  if (dpyinfo->gdpy)
 +    {
 +      image_destroy_all_bitmaps (dpyinfo);
 +
 +      g_clear_object (&dpyinfo->xg_cursor);
 +      g_clear_object (&dpyinfo->vertical_scroll_bar_cursor);
 +      g_clear_object (&dpyinfo->horizontal_scroll_bar_cursor);
 +      g_clear_object (&dpyinfo->invisible_cursor);
 +      if (dpyinfo->last_click_event != NULL) {
 +      gdk_event_free (dpyinfo->last_click_event);
 +      dpyinfo->last_click_event = NULL;
 +      }
 +
 +      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;
 +    }
 +
 +  delete_keyboard_wait_descriptor (0);
 +
 +  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);
 +    }
 +}
 +
 +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 && wid != NULL)
 +    {
 +      block_input ();
 +      gtk_window_present (GTK_WINDOW (wid));
 +      unblock_input ();
 +    }
 +}
 +
 +
 +static void
 +set_opacity_recursively (GtkWidget * w, gpointer data)
 +{
 +  gtk_widget_set_opacity (w, *(double *) data);
 +  if (GTK_IS_CONTAINER (w))
 +    gtk_container_foreach (GTK_CONTAINER (w), set_opacity_recursively, data);
 +}
 +
 +static void
 +x_set_frame_alpha (struct frame *f)
 +{
 +  struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
 +  double alpha = 1.0;
 +  double alpha_min = 1.0;
 +
 +  if (dpyinfo->highlight_frame == f)
 +    alpha = f->alpha[0];
 +  else
 +    alpha = f->alpha[1];
 +
 +  if (alpha < 0.0)
 +    return;
 +
 +  if (FLOATP (Vframe_alpha_lower_limit))
 +    alpha_min = XFLOAT_DATA (Vframe_alpha_lower_limit);
 +  else if (FIXNUMP (Vframe_alpha_lower_limit))
 +    alpha_min = (XFIXNUM (Vframe_alpha_lower_limit)) / 100.0;
 +
 +  if (alpha > 1.0)
 +    alpha = 1.0;
 +  else if (alpha < alpha_min && alpha_min <= 1.0)
 +    alpha = alpha_min;
 +
 +#if 0
 +  /* If there is a parent from the window manager, put the property there
 +     also, to work around broken window managers that fail to do that.
 +     Do this unconditionally as this function is called on reparent when
 +     alpha has not changed on the frame.  */
 +
 +  if (!FRAME_PARENT_FRAME (f))
 +    {
 +      Window parent = x_find_topmost_parent (f);
 +      if (parent != None)
 +      XChangeProperty (dpy, parent, dpyinfo->Xatom_net_wm_window_opacity,
 +                       XA_CARDINAL, 32, PropModeReplace,
 +                       (unsigned char *) &opac, 1);
 +    }
 +#endif
 +
 +  set_opacity_recursively (FRAME_WIDGET (f), &alpha);
 +  /* without this, blending mode is strange on wayland. */
 +  gtk_widget_queue_resize_no_redraw (FRAME_WIDGET (f));
 +}
 +
 +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).  */
 +
 +  GtkWidget *w = FRAME_WIDGET (f);
 +
 +  char *css =
 +    g_strdup_printf ("decoration { border: solid %dpx #%06x; }",
 +                   f->border_width,
 +                   (unsigned int) FRAME_X_OUTPUT (f)->border_pixel & 0x00ffffff);
 +
 +  GtkStyleContext *ctxt = gtk_widget_get_style_context (w);
 +  GtkCssProvider *css_provider = gtk_css_provider_new ();
 +  gtk_css_provider_load_from_data (css_provider, css, -1, NULL);
 +  gtk_style_context_add_provider (ctxt, GTK_STYLE_PROVIDER (css_provider),
 +                                GTK_STYLE_PROVIDER_PRIORITY_USER);
 +  g_free (css);
 +
 +  GtkCssProvider *old = FRAME_X_OUTPUT (f)->border_color_css_provider;
 +  FRAME_X_OUTPUT (f)->border_color_css_provider = css_provider;
 +  if (old != NULL)
 +    {
 +      gtk_style_context_remove_provider (ctxt, GTK_STYLE_PROVIDER (old));
 +      g_object_unref (old);
 +    }
 +
 +  unblock_input ();
 +  gui_update_cursor (f, true);
 +  x_set_frame_alpha (f);
 +}
 +
 +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).  */
 +
 +  GtkWidget *w = FRAME_WIDGET (f);
 +
 +  char *css =
 +    g_strdup_printf ("decoration { border: dotted %dpx #ffffff; }",
 +                   f->border_width);
 +
 +  GtkStyleContext *ctxt = gtk_widget_get_style_context (w);
 +  GtkCssProvider *css_provider = gtk_css_provider_new ();
 +  gtk_css_provider_load_from_data (css_provider, css, -1, NULL);
 +  gtk_style_context_add_provider (ctxt, GTK_STYLE_PROVIDER (css_provider),
 +                                GTK_STYLE_PROVIDER_PRIORITY_USER);
 +  g_free (css);
 +
 +  GtkCssProvider *old = FRAME_X_OUTPUT (f)->border_color_css_provider;
 +  FRAME_X_OUTPUT (f)->border_color_css_provider = css_provider;
 +  if (old != NULL)
 +    {
 +      gtk_style_context_remove_provider (ctxt, GTK_STYLE_PROVIDER (old));
 +      g_object_unref (old);
 +    }
 +
 +  unblock_input ();
 +  gui_update_cursor (f, true);
 +  x_set_frame_alpha (f);
 +}
 +
 +
 +void
 +pgtk_frame_rehighlight (struct pgtk_display_info *dpyinfo)
 +{
 +  struct frame *old_highlight = dpyinfo->highlight_frame;
 +
 +  if (dpyinfo->x_focus_frame)
 +    {
 +      dpyinfo->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->highlight_frame))
 +      {
 +        fset_focus_frame (dpyinfo->x_focus_frame, Qnil);
 +        dpyinfo->highlight_frame = dpyinfo->x_focus_frame;
 +      }
 +    }
 +  else
 +    dpyinfo->highlight_frame = 0;
 +
 +  if (old_highlight)
 +    frame_unhighlight (old_highlight);
 +  if (dpyinfo->highlight_frame)
 +    frame_highlight (dpyinfo->highlight_frame);
 +}
 +
 +/* The focus has changed, or we have redirected a frame's focus to
 +   another frame (this happens when a frame uses a surrogate
 +   mini-buffer frame).  Shift the highlight as appropriate.
 +
 +   The FRAME argument doesn't necessarily have anything to do with which
 +   frame is being highlighted or un-highlighted; we only use it to find
 +   the appropriate X display info.  */
 +
 +static void
 +XTframe_rehighlight (struct frame *frame)
 +{
 +  pgtk_frame_rehighlight (FRAME_DISPLAY_INFO (frame));
 +}
 +
 +
 +/* Toggle mouse pointer visibility on frame F by using invisible cursor.  */
 +
 +static void
 +x_toggle_visible_pointer (struct frame *f, bool invisible)
 +{
 +  Emacs_Cursor cursor;
 +  if (invisible)
 +    cursor = FRAME_DISPLAY_INFO (f)->invisible_cursor;
 +  else
 +    cursor = f->output_data.pgtk->current_cursor;
 +  gdk_window_set_cursor (gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
 +                       cursor);
 +  f->pointer_invisible = invisible;
 +}
 +
 +static void
 +x_setup_pointer_blanking (struct pgtk_display_info *dpyinfo)
 +{
 +  dpyinfo->toggle_visible_pointer = x_toggle_visible_pointer;
 +  dpyinfo->invisible_cursor =
 +    gdk_cursor_new_for_display (dpyinfo->gdpy, GDK_BLANK_CURSOR);
 +}
 +
 +static void
 +XTtoggle_invisible_pointer (struct frame *f, bool invisible)
 +{
 +  block_input ();
 +  FRAME_DISPLAY_INFO (f)->toggle_visible_pointer (f, invisible);
 +  unblock_input ();
 +}
 +
 +/* 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;
 +  /* doesn't work on wayland */
 +
 +  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 (old_focus && old_focus->auto_lower)
 +      if (FRAME_GTK_OUTER_WIDGET (old_focus))
 +        gdk_window_lower (gtk_widget_get_window
 +                          (FRAME_GTK_OUTER_WIDGET (old_focus)));
 +
 +      if (dpyinfo->x_focus_frame && dpyinfo->x_focus_frame->auto_raise)
 +      if (FRAME_GTK_OUTER_WIDGET (dpyinfo->x_focus_frame))
 +        gdk_window_raise (gtk_widget_get_window
 +                          (FRAME_GTK_OUTER_WIDGET (dpyinfo->x_focus_frame)));
 +    }
 +
 +  pgtk_frame_rehighlight (dpyinfo);
 +}
 +
 +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->toggle_invisible_pointer_hook = XTtoggle_invisible_pointer;
 +  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 = XTframe_rehighlight;
 +  // terminal->frame_raise_lower_hook = pgtk_frame_raise_lower;
 +  terminal->frame_visible_invisible_hook = pgtk_make_frame_visible_invisible;
 +  terminal->fullscreen_hook = pgtk_fullscreen_hook;
 +  terminal->menu_show_hook = pgtk_menu_show;
 +  terminal->activate_menubar_hook = pgtk_activate_menubar;
 +  terminal->popup_dialog_hook = pgtk_popup_dialog;
 +  terminal->change_tab_bar_height_hook = x_change_tab_bar_height;
 +  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->get_string_resource_hook = pgtk_get_string_resource;
 +  terminal->delete_frame_hook = x_destroy_window;
 +  terminal->delete_terminal_hook = pgtk_delete_terminal;
 +  terminal->query_frame_background_color = pgtk_query_frame_background_color;
 +  terminal->defined_color_hook = pgtk_defined_color;
 +  terminal->set_new_font_hook = pgtk_new_font;
 +  terminal->set_bitmap_icon_hook = pgtk_bitmap_icon;
 +  terminal->implicit_set_name_hook = pgtk_implicitly_set_name;
 +  terminal->iconify_frame_hook = pgtk_iconify_frame;
 +  terminal->set_scroll_bar_default_width_hook =
 +    pgtk_set_scroll_bar_default_width;
 +  terminal->set_scroll_bar_default_height_hook =
 +    pgtk_set_scroll_bar_default_height;
 +  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;
 +  GtkWidget *emacs_gtk_fixed; // stop on emacsgtkfixed other than this.
 +};
 +
 +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 (EMACS_IS_FIXED (widget) && widget != datap->emacs_gtk_fixed)
 +    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;
 +  data.emacs_gtk_fixed = FRAME_GTK_WIDGET (f);
 +  pgtk_window_is_of_frame_recursive (FRAME_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 = FRAME_TOP_MARGIN_HEIGHT (f);
 +      int face_id =
 +      (FRAME_PARENT_FRAME (f)
 +       ? (!NILP (Vface_remapping_alist)
 +          ? lookup_basic_face (NULL, f, CHILD_FRAME_BORDER_FACE_ID)
 +          : CHILD_FRAME_BORDER_FACE_ID)
 +       : (!NILP (Vface_remapping_alist)
 +          ? lookup_basic_face (NULL, f, INTERNAL_BORDER_FACE_ID)
 +          : INTERNAL_BORDER_FACE_ID));
 +      struct face *face = FACE_FROM_ID_OR_NULL (f, face_id);
 +
 +      block_input ();
 +
 +      if (face)
 +      {
 +#define x_fill_rectangle(f, gc, x, y, w, h) \
 +          fill_background_by_face (f, face, x, y, w, h)
 +        x_fill_rectangle (f, gc, 0, margin, width, border);
 +        x_fill_rectangle (f, gc, 0, 0, border, height);
 +        x_fill_rectangle (f, gc, width - border, 0, border, height);
 +        x_fill_rectangle (f, gc, 0, height - border, width, border);
 +#undef x_fill_rectangle
 +      }
 +      else
 +      {
 +#define x_clear_area(f, x, y, w, h)  pgtk_clear_area (f, x, y, w, h)
 +        x_clear_area (f, 0, 0, border, height);
 +        x_clear_area (f, 0, margin, width, border);
 +        x_clear_area (f, width - border, 0, border, height);
 +        x_clear_area (f, 0, height - border, width, border);
 +#undef x_clear_area
 +      }
 +
 +      unblock_input ();
 +    }
 +}
 +
 +#ifdef HAVE_PGTK
 +
 +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);
 +}
 +
 +#endif
 +
 +static gboolean
 +pgtk_handle_draw (GtkWidget * widget, cairo_t * cr, gpointer * data)
 +{
 +  struct frame *f;
 +
 +  PGTK_TRACE ("pgtk_handle_draw");
 +
 +#ifdef HAVE_PGTK
 +  print_widget_tree (widget);
 +#endif
 +
 +  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 && FRAME_CR_ACTIVE_CONTEXT (f) != NULL)
 +          src = cairo_get_target (FRAME_CR_ACTIVE_CONTEXT (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);
 +      pgtk_cr_update_surface_desired_size (f, alloc->width, alloc->height);
 +    }
 +}
 +
 +static void
 +x_find_modifier_meanings (struct pgtk_display_info *dpyinfo)
 +{
 +  GdkDisplay *gdpy = dpyinfo->gdpy;
 +  GdkKeymap *keymap = gdk_keymap_get_for_display (gdpy);
 +  GdkModifierType state = GDK_META_MASK;
 +  gboolean r = gdk_keymap_map_virtual_modifiers (keymap, &state);
 +  if (r)
 +    {
 +      /* Meta key exists. */
 +      if (state == GDK_META_MASK)
 +      {
 +        dpyinfo->meta_mod_mask = GDK_MOD1_MASK;       /* maybe this is meta. */
 +        dpyinfo->alt_mod_mask = 0;
 +      }
 +      else
 +      {
 +        dpyinfo->meta_mod_mask = state & ~GDK_META_MASK;
 +        if (dpyinfo->meta_mod_mask == GDK_MOD1_MASK)
 +          dpyinfo->alt_mod_mask = 0;
 +        else
 +          dpyinfo->alt_mod_mask = GDK_MOD1_MASK;
 +      }
 +    }
 +  else
 +    {
 +      dpyinfo->meta_mod_mask = GDK_MOD1_MASK;
 +      dpyinfo->alt_mod_mask = 0;
 +    }
 +
 +  state = GDK_SUPER_MASK;
 +  r = gdk_keymap_map_virtual_modifiers (keymap, &state);
 +  if (r)
 +    {
 +      /* Super key exists. */
 +      if (state == GDK_SUPER_MASK)
 +      {
 +        dpyinfo->super_mod_mask = GDK_MOD4_MASK;      /* maybe this is super. */
 +      }
 +      else
 +      {
 +        dpyinfo->super_mod_mask = state & ~GDK_SUPER_MASK;
 +      }
 +    }
 +  else
 +    {
 +      dpyinfo->super_mod_mask = GDK_MOD4_MASK;
 +    }
 +
 +  state = GDK_HYPER_MASK;
 +  r = gdk_keymap_map_virtual_modifiers (keymap, &state);
 +  if (r)
 +    {
 +      /* Hyper key exists. */
 +      if (state == GDK_HYPER_MASK)
 +      {
 +        dpyinfo->hyper_mod_mask = GDK_MOD3_MASK;      /* maybe this is hyper. */
 +      }
 +      else
 +      {
 +        dpyinfo->hyper_mod_mask = state & ~GDK_HYPER_MASK;
 +      }
 +    }
 +  else
 +    {
 +      dpyinfo->hyper_mod_mask = GDK_MOD3_MASK;
 +    }
 +}
 +
 +static void
 +get_modifier_values (int *mod_ctrl,
 +                   int *mod_meta,
 +                   int *mod_alt, int *mod_hyper, int *mod_super)
 +{
 +  Lisp_Object tem;
 +
 +  *mod_ctrl = ctrl_modifier;
 +  *mod_meta = meta_modifier;
 +  *mod_alt = alt_modifier;
 +  *mod_hyper = hyper_modifier;
 +  *mod_super = super_modifier;
 +
 +  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;
 +}
 +
 +int
 +pgtk_gtk_to_emacs_modifiers (struct pgtk_display_info *dpyinfo, int state)
 +{
 +  int mod_ctrl;
 +  int mod_meta;
 +  int mod_alt;
 +  int mod_hyper;
 +  int mod_super;
 +  int mod;
 +
 +  get_modifier_values (&mod_ctrl, &mod_meta, &mod_alt, &mod_hyper,
 +                     &mod_super);
 +
 +  mod = 0;
 +  if (state & GDK_SHIFT_MASK)
 +    mod |= shift_modifier;
 +  if (state & GDK_CONTROL_MASK)
 +    mod |= mod_ctrl;
 +  if (state & dpyinfo->meta_mod_mask)
 +    mod |= mod_meta;
 +  if (state & dpyinfo->alt_mod_mask)
 +    mod |= mod_alt;
 +  if (state & dpyinfo->super_mod_mask)
 +    mod |= mod_super;
 +  if (state & dpyinfo->hyper_mod_mask)
 +    mod |= mod_hyper;
 +  return mod;
 +}
 +
 +static int
 +pgtk_emacs_to_gtk_modifiers (struct pgtk_display_info *dpyinfo, int state)
 +{
 +  int mod_ctrl;
 +  int mod_meta;
 +  int mod_alt;
 +  int mod_hyper;
 +  int mod_super;
 +  int mask;
 +
 +  get_modifier_values (&mod_ctrl, &mod_meta, &mod_alt, &mod_hyper,
 +                     &mod_super);
 +
 +  mask = 0;
 +  if (state & mod_alt)
 +    mask |= dpyinfo->alt_mod_mask;
 +  if (state & mod_super)
 +    mask |= dpyinfo->super_mod_mask;
 +  if (state & mod_hyper)
 +    mask |= dpyinfo->hyper_mod_mask;
 +  if (state & shift_modifier)
 +    mask |= GDK_SHIFT_MASK;
 +  if (state & mod_ctrl)
 +    mask |= GDK_CONTROL_MASK;
 +  if (state & mod_meta)
 +    mask |= dpyinfo->meta_mod_mask;
 +  return mask;
 +}
 +
 +#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)
 +#define IsModifierKey(keysym)                                                 \
 +  ((((keysym) >= GDK_KEY_Shift_L) && ((keysym) <= GDK_KEY_Hyper_R))           \
 +   || (((keysym) >= GDK_KEY_ISO_Lock) && ((keysym) <= GDK_KEY_ISO_Level5_Lock))       \
 +   || ((keysym) == GDK_KEY_Mode_switch)                                               \
 +   || ((keysym) == GDK_KEY_Num_Lock))
 +
 +
 +void
 +pgtk_enqueue_string (struct frame *f, gchar * str)
 +{
 +  gunichar *ustr;
 +
 +  ustr = g_utf8_to_ucs4 (str, -1, NULL, NULL, NULL);
 +  if (ustr == NULL)
 +    return;
 +  for (; *ustr != 0; ustr++)
 +    {
 +      union buffered_input_event inev;
 +      Lisp_Object c = make_fixnum (*ustr);
 +      EVENT_INIT (inev.ie);
 +      inev.ie.kind = (SINGLE_BYTE_CHAR_P (XFIXNAT (c))
 +                    ? ASCII_KEYSTROKE_EVENT
 +                    : MULTIBYTE_CHAR_KEYSTROKE_EVENT);
 +      inev.ie.arg = Qnil;
 +      inev.ie.code = XFIXNAT (c);
 +      XSETFRAME (inev.ie.frame_or_window, f);
 +      inev.ie.modifiers = 0;
 +      inev.ie.timestamp = 0;
 +      evq_enqueue (&inev);
 +    }
 +
 +}
 +
 +void
 +pgtk_enqueue_preedit (struct frame *f, Lisp_Object preedit)
 +{
 +  union buffered_input_event inev;
 +  EVENT_INIT (inev.ie);
 +  inev.ie.kind = PGTK_PREEDIT_TEXT_EVENT;
 +  inev.ie.arg = preedit;
 +  inev.ie.code = 0;
 +  XSETFRAME (inev.ie.frame_or_window, f);
 +  inev.ie.modifiers = 0;
 +  inev.ie.timestamp = 0;
 +  evq_enqueue (&inev);
 +}
 +
 +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)
 +    {
 +      /* While super is pressed, gtk_im_context_filter_keypress() always process the
 +       * key events ignoring super.
 +       * As a work around, don't call it while super or hyper are pressed...
 +       */
 +      struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
 +      if (!(event->key.state & (dpyinfo->super_mod_mask | dpyinfo->hyper_mod_mask)))
 +      {
 +        if (pgtk_im_filter_keypress (f, &event->key))
 +          return 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 (FRAME_DISPLAY_INFO (f),
 +                                   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 (FRAME_DISPLAY_INFO (f), 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
 +             /* Gtk's modifier keys are different from Xlib's ones.
 +              * I need to exclude them.
 +              */
 +             || IsModifierKey (orig_keysym)
 +             /* 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 locale `coding_system'. */
 +          if (!NILP (Vlocale_coding_system))
 +            coding_system = Vlocale_coding_system;
 +          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))
 +    {
 +      if (any_help_event_p)
 +      {
 +        Lisp_Object frame;
 +        if (f)
 +          XSETFRAME (frame, f);
 +        else
 +          frame = Qnil;
 +        help_echo_string = Qnil;
 +        gen_help_event (Qnil, frame, Qnil, Qnil, 0);
 +      }
 +    }
 +  return FALSE;
 +}
 +
 +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);
 +      }
 +    }
 +
 +  if (inev.ie.kind != NO_EVENT)
 +    evq_enqueue (&inev);
 +  return FALSE;
 +}
 +
 +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 FALSE;
 +}
 +
 +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;
 +}
 +
 +/* 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 (frame->pointer_invisible)
 +      XTtoggle_invisible_pointer (frame, false);
 +    }
 +}
 +
 +static gboolean
 +enter_notify_event (GtkWidget * widget, GdkEvent * event,
 +                  gpointer * user_data)
 +{
 +  PGTK_TRACE ("enter_notify_event");
 +  union buffered_input_event inev;
 +  struct frame *frame =
 +    pgtk_any_window_to_frame (gtk_widget_get_window (widget));
 +  if (frame == NULL)
 +    return FALSE;
 +  struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
 +  struct frame *focus_frame = dpyinfo->x_focus_frame;
 +  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 (event->crossing.detail != GDK_NOTIFY_INFERIOR
 +      && event->crossing.focus && !(focus_state & FOCUS_EXPLICIT))
 +    x_focus_changed (TRUE, FOCUS_IMPLICIT, dpyinfo, 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 *frame =
 +    pgtk_any_window_to_frame (gtk_widget_get_window (widget));
 +  if (frame == NULL)
 +    return FALSE;
 +  struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
 +  struct frame *focus_frame = dpyinfo->x_focus_frame;
 +  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 (event->crossing.detail != GDK_NOTIFY_INFERIOR
 +      && event->crossing.focus && !(focus_state & FOCUS_EXPLICIT))
 +    x_focus_changed (FALSE, FOCUS_IMPLICIT, dpyinfo, frame, &inev);
 +
 +  if (frame)
 +    {
 +      if (any_help_event_p)
 +      {
 +        Lisp_Object frame_obj;
 +        XSETFRAME (frame_obj, frame);
 +        help_echo_string = Qnil;
 +        gen_help_event (Qnil, frame_obj, Qnil, Qnil, 0);
 +      }
 +    }
 +
 +  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_EXPLICIT,
 +                 FRAME_DISPLAY_INFO (frame), frame, &inev);
 +  if (inev.ie.kind != NO_EVENT)
 +    evq_enqueue (&inev);
 +
 +  pgtk_im_focus_in (frame);
 +
 +  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_EXPLICIT,
 +                 FRAME_DISPLAY_INFO (frame), frame, &inev);
 +  if (inev.ie.kind != NO_EVENT)
 +    evq_enqueue (&inev);
 +
 +  pgtk_im_focus_out (frame);
 +
 +  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;
 +
 +  /* This is needed to make pointer visible when motion_notify event */
 +  pending_signals = true;
 +
 +  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 = (gui_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, 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;
 +
 +      if (f)
 +      XSETFRAME (frame, f);
 +      else
 +      frame = Qnil;
 +
 +      any_help_event_p = true;
 +      gen_help_event (help_echo_string, frame, help_echo_window,
 +                    help_echo_object, help_echo_pos);
 +    }
 +
 +  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 (FRAME_DISPLAY_INFO (f), 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 tab_bar_p = false;
 +  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 (gui_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->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 ();
 +          }
 +      }
 +    }
 +
 +  /* xg_event_is_for_scrollbar() doesn't work correctly on sway, and
 +   * we shouldn't need it.
 +   */
 +#if 0
 +  if (f && xg_event_is_for_scrollbar (f, event))
 +    f = 0;
 +#endif
 +
 +  if (f)
 +    {
 +      /* Is this in the tab-bar?  */
 +      if (WINDOWP (f->tab_bar_window)
 +        && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)))
 +      {
 +        Lisp_Object window;
 +        int x = event->button.x;
 +        int y = event->button.y;
 +
 +        window = window_from_coordinates (f, x, y, 0, true, true);
 +        tab_bar_p = EQ (window, f->tab_bar_window);
 +
 +        if (tab_bar_p && event->button.button < 4)
 +          handle_tab_bar_click
 +            (f, x, y, event->type == GDK_BUTTON_PRESS,
 +             pgtk_gtk_to_emacs_modifiers (dpyinfo, event->button.state));
 +      }
 +    }
 +
 +  if (f)
 +    {
 +      if (!tab_bar_p && !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;
 +
 +      if (dpyinfo->last_click_event != NULL)
 +      gdk_event_free (dpyinfo->last_click_event);
 +      dpyinfo->last_click_event = gdk_event_copy (event);
 +    }
 +  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;
 +  GdkScrollDirection dir;
 +  double delta_x, delta_y;
 +
 +  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 (gui_mouse_grabbed (dpyinfo))
 +    f = dpyinfo->last_mouse_frame;
 +  else
 +    f = pgtk_any_window_to_frame (gtk_widget_get_window (widget));
 +
 +  inev.ie.kind = NO_EVENT;
 +  inev.ie.timestamp = event->scroll.time;
 +  inev.ie.modifiers =
 +    pgtk_gtk_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), 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;
 +
 +  if (gdk_event_get_scroll_direction (event, &dir))
 +    {
 +      switch (dir)
 +      {
 +      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:         /* shut up warning */
 +        break;
 +      }
 +    }
 +  else if (gdk_event_get_scroll_deltas (event, &delta_x, &delta_y))
 +    {
 +      dpyinfo->scroll.acc_x += delta_x;
 +      dpyinfo->scroll.acc_y += delta_y;
 +      if (dpyinfo->scroll.acc_y >= dpyinfo->scroll.y_per_line)
 +      {
 +        int nlines = dpyinfo->scroll.acc_y / dpyinfo->scroll.y_per_line;
 +        inev.ie.kind = WHEEL_EVENT;
 +        inev.ie.modifiers |= down_modifier;
 +        inev.ie.arg = make_fixnum(nlines);
 +        dpyinfo->scroll.acc_y -= dpyinfo->scroll.y_per_line * nlines;
 +      }
 +      else if (dpyinfo->scroll.acc_y <= -dpyinfo->scroll.y_per_line)
 +      {
 +        int nlines = -dpyinfo->scroll.acc_y / dpyinfo->scroll.y_per_line;
 +        inev.ie.kind = WHEEL_EVENT;
 +        inev.ie.modifiers |= up_modifier;
 +        inev.ie.arg = make_fixnum(nlines);
 +        dpyinfo->scroll.acc_y -= -dpyinfo->scroll.y_per_line * nlines;
 +      }
 +      else if (dpyinfo->scroll.acc_x >= dpyinfo->scroll.x_per_char)
 +      {
 +        int nchars = dpyinfo->scroll.acc_x / dpyinfo->scroll.x_per_char;
 +        inev.ie.kind = HORIZ_WHEEL_EVENT;
 +        inev.ie.modifiers |= up_modifier;
 +        inev.ie.arg = make_fixnum(nchars);
 +        dpyinfo->scroll.acc_x -= dpyinfo->scroll.x_per_char * nchars;
 +      }
 +      else if (dpyinfo->scroll.acc_x <= -dpyinfo->scroll.x_per_char)
 +      {
 +        int nchars = -dpyinfo->scroll.acc_x / dpyinfo->scroll.x_per_char;
 +        inev.ie.kind = HORIZ_WHEEL_EVENT;
 +        inev.ie.modifiers |= down_modifier;
 +        inev.ie.arg = make_fixnum(nchars);
 +        dpyinfo->scroll.acc_x -= -dpyinfo->scroll.x_per_char * nchars;
 +      }
 +    }
 +
 +  if (inev.ie.kind != NO_EVENT)
 +    evq_enqueue (&inev);
 +  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)
 +{
 +  if (f->tooltip)
 +    {
 +      g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "draw",
 +                      G_CALLBACK (pgtk_handle_draw), NULL);
 +      return;
 +    }
 +
 +  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));
 +
 +  if (FRAME_GTK_OUTER_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)), "event",
 +                      G_CALLBACK (pgtk_handle_event), NULL);
 +      g_signal_connect (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)), "configure-event",
 +                      G_CALLBACK (configure_event), NULL);
 +    }
 +
 +  g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "map-event",
 +                  G_CALLBACK (map_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-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'));
 +}
 +
 +#define GNOME_INTERFACE_SCHEMA "org.gnome.desktop.interface"
 +
 +static gdouble pgtk_text_scaling_factor(void)
 +{
 +  GSettingsSchemaSource *schema_source = g_settings_schema_source_get_default ();
 +  if (schema_source != NULL)
 +    {
 +      GSettingsSchema *schema = g_settings_schema_source_lookup (schema_source,
 +         GNOME_INTERFACE_SCHEMA, true);
 +      if (schema != NULL)
 +        {
 +        g_settings_schema_unref (schema);
 +        GSettings *set = g_settings_new (GNOME_INTERFACE_SCHEMA);
 +        return g_settings_get_double (set, "text-scaling-factor");
 +      }
 +    }
 +  return 1;
 +}
 +
 +
 +/* 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;
 +  static char *initial_display = NULL;
 +  char *dpy_name;
 +  Lisp_Object lisp_dpy_name = Qnil;
 +
 +  block_input ();
 +
 +  if (!x_initialized)
 +    {
 +      any_help_event_p = false;
 +
 +      Fset_input_interrupt_mode (Qt);
 +      baud_rate = 19200;
 +
 +#ifdef USE_CAIRO
 +      gui_init_fringe (&pgtk_redisplay_interface);
 +#endif
 +
 +      ++x_initialized;
 +    }
 +
 +  dpy_name = SSDATA (display_name);
 +  if (strlen (dpy_name) == 0 && initial_display != NULL)
 +    dpy_name = initial_display;
 +  lisp_dpy_name = build_string (dpy_name);
 +
 +  {
 +#define NUM_ARGV 10
 +    int argc;
 +    char *argv[NUM_ARGV];
 +    char **argv2 = argv;
 +    guint id;
 +
 +    if (x_initialized++ > 1)
 +      {
 +      xg_display_open (dpy_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 (dpy_name) != 0)
 +        {
 +          argv[argc++] = display_opt;
 +          argv[argc++] = dpy_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 ();
 +
 +      initial_display = g_strdup (gdk_display_get_name (dpy));
 +      dpy_name = initial_display;
 +      lisp_dpy_name = build_string (dpy_name);
 +      }
 +  }
 +
 +  /* 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)), dpy_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 (lisp_dpy_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 (lisp_dpy_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);
 +
 +  /* Figure out which modifier bits mean what.  */
 +  x_find_modifier_meanings (dpyinfo);
 +
 +  /* 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);
 +
 +  dpyinfo->vertical_scroll_bar_cursor
 +    = gdk_cursor_new_for_display (dpyinfo->gdpy, GDK_SB_V_DOUBLE_ARROW);
 +
 +  dpyinfo->horizontal_scroll_bar_cursor
 +    = gdk_cursor_new_for_display (dpyinfo->gdpy, GDK_SB_H_DOUBLE_ARROW);
 +
 +  dpyinfo->icon_bitmap_id = -1;
 +
 +  reset_mouse_highlight (&dpyinfo->mouse_highlight);
 +
 +  {
 +    GdkScreen *gscr = gdk_display_get_default_screen (dpyinfo->gdpy);
 +
 +    gdouble dpi = 96.0 * pgtk_text_scaling_factor();
 +    gdk_screen_set_resolution (gscr, dpi);
 +    dpyinfo->resx = dpi;
 +    dpyinfo->resy = dpi;
 +  }
 +
 +  /* smooth scroll setting */
 +  dpyinfo->scroll.x_per_char = 2;
 +  dpyinfo->scroll.y_per_line = 2;
 +
 +  x_setup_pointer_blanking (dpyinfo);
 +
 +  xsettings_initialize (dpyinfo);
 +
 +  pgtk_selection_init ();
 +
 +  pgtk_im_init (dpyinfo);
 +
 +  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 = xg_check_special_colors (f, name, color_def);
 +  if (!r)
 +    r = pgtk_parse_color (f, 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 (struct frame *f, 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 =
 +      (color->red >> 8) << 16 |
 +      (color->green >> 8) << 8 |
 +      (color->blue >> 8) << 0;
 +      return 1;
 +    }
 +  return 0;
 +}
 +
 +/* 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;
 +      PGTK_TRACE ("pixel: %lx, red: %d, blue %d, green %d", colors[i].pixel,
 +                colors[i].red, colors[i].blue, colors[i].green);
 +    }
 +}
 +
 +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");
 +
 +  xg_default_icon_file =
 +    build_pure_c_string ("icons/hicolor/scalable/apps/emacs.svg");
 +  staticpro (&xg_default_icon_file);
 +
 +  DEFSYM (Qx_gtk_map_stock, "x-gtk-map-stock");
 +
 +
 +  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;
 +
 +
 +  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 does not allow resizing a surface/context after it is
 + * created, so we need to trash the old context, create a new context
 + * on the next cr_clip_begin with the new dimensions and request a
 + * re-draw.
 + *
 + * This Will leave the active context available to present on screen
 + * until a redrawn frame is completed.
 + */
 +void
 +pgtk_cr_update_surface_desired_size (struct frame *f, int width, int height)
 +{
 +  PGTK_TRACE ("pgtk_cr_update_surface_desired_size");
 +
 +  if (FRAME_CR_SURFACE_DESIRED_WIDTH (f) != width
 +      || FRAME_CR_SURFACE_DESIRED_HEIGHT (f) != height)
 +    {
 +      pgtk_cr_destroy_frame_context (f);
 +      FRAME_CR_SURFACE_DESIRED_WIDTH (f) = width;
 +      FRAME_CR_SURFACE_DESIRED_HEIGHT (f) = height;
 +      SET_FRAME_GARBAGED (f);
 +    }
 +}
 +
 +
 +cairo_t *
 +pgtk_begin_cr_clip (struct frame *f)
 +{
 +  cairo_t *cr = FRAME_CR_CONTEXT (f);
 +
 +  PGTK_TRACE ("pgtk_begin_cr_clip");
 +  if (!cr)
 +    {
 +      cairo_surface_t *surface =
 +      gdk_window_create_similar_surface (gtk_widget_get_window
 +                                         (FRAME_GTK_WIDGET (f)),
 +                                         CAIRO_CONTENT_COLOR_ALPHA,
 +                                         FRAME_CR_SURFACE_DESIRED_WIDTH (f),
 +                                         FRAME_CR_SURFACE_DESIRED_HEIGHT
 +                                         (f));
 +
 +      cr = FRAME_CR_CONTEXT (f) = cairo_create (surface);
 +      cairo_surface_destroy (surface);
 +    }
 +
 +  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));
 +}
 +
 +void
 +pgtk_set_cr_source_with_gc_foreground (struct frame *f, Emacs_GC * 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, Emacs_GC * 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);
 +}
 +
 +static cairo_status_t
 +pgtk_cr_accumulate_data (void *closure, const unsigned char *data,
 +                    unsigned int length)
 +{
 +  Lisp_Object *acc = (Lisp_Object *) closure;
 +
 +  *acc = Fcons (make_unibyte_string ((char const *) data, length), *acc);
 +
 +  return CAIRO_STATUS_SUCCESS;
 +}
 +
 +void
 +pgtk_cr_destroy_frame_context (struct frame *f)
 +{
 +  PGTK_TRACE ("pgtk_cr_destroy_frame_context");
 +  if (FRAME_CR_CONTEXT (f) != NULL)
 +    {
 +      cairo_destroy (FRAME_CR_CONTEXT (f));
 +      FRAME_CR_CONTEXT (f) = NULL;
 +    }
 +}
 +
 +static void
 +pgtk_cr_destroy (void *cr)
 +{
 +  block_input ();
 +  cairo_destroy (cr);
 +  unblock_input ();
 +}
 +
 +
 +
 +Lisp_Object
 +pgtk_cr_export_frames (Lisp_Object frames, cairo_surface_type_t surface_type)
 +{
 +  struct frame *f;
 +  cairo_surface_t *surface;
 +  cairo_t *cr;
 +  int width, height;
 +  void (*surface_set_size_func) (cairo_surface_t *, double, double) = NULL;
 +  Lisp_Object acc = Qnil;
 +  ptrdiff_t count = SPECPDL_INDEX ();
 +
 +  specbind (Qredisplay_dont_pause, Qt);
 +  redisplay_preserve_echo_area (31);
 +
 +  f = XFRAME (XCAR (frames));
 +  frames = XCDR (frames);
 +  width = FRAME_PIXEL_WIDTH (f);
 +  height = FRAME_PIXEL_HEIGHT (f);
 +
 +  block_input ();
 +#ifdef CAIRO_HAS_PDF_SURFACE
 +  if (surface_type == CAIRO_SURFACE_TYPE_PDF)
 +    {
 +      surface = cairo_pdf_surface_create_for_stream (pgtk_cr_accumulate_data, &acc,
 +                                                   width, height);
 +      surface_set_size_func = cairo_pdf_surface_set_size;
 +    }
 +  else
 +#endif
 +#ifdef CAIRO_HAS_PNG_FUNCTIONS
 +  if (surface_type == CAIRO_SURFACE_TYPE_IMAGE)
 +    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
 +  else
 +#endif
 +#ifdef CAIRO_HAS_PS_SURFACE
 +  if (surface_type == CAIRO_SURFACE_TYPE_PS)
 +    {
 +      surface = cairo_ps_surface_create_for_stream (pgtk_cr_accumulate_data, &acc,
 +                                                  width, height);
 +      surface_set_size_func = cairo_ps_surface_set_size;
 +    }
 +  else
 +#endif
 +#ifdef CAIRO_HAS_SVG_SURFACE
 +  if (surface_type == CAIRO_SURFACE_TYPE_SVG)
 +    surface = cairo_svg_surface_create_for_stream (pgtk_cr_accumulate_data, &acc,
 +                                                 width, height);
 +  else
 +#endif
 +    abort ();
 +
 +  cr = cairo_create (surface);
 +  cairo_surface_destroy (surface);
 +  record_unwind_protect_ptr (pgtk_cr_destroy, cr);
 +
 +  while (1)
 +    {
 +      cairo_t *saved_cr = FRAME_CR_CONTEXT (f);
 +      FRAME_CR_CONTEXT (f) = cr;
 +      pgtk_clear_area (f, 0, 0, width, height);
 +      expose_frame (f, 0, 0, width, height);
 +      FRAME_CR_CONTEXT (f) = saved_cr;
 +
 +      if (NILP (frames))
 +      break;
 +
 +      cairo_surface_show_page (surface);
 +      f = XFRAME (XCAR (frames));
 +      frames = XCDR (frames);
 +      width = FRAME_PIXEL_WIDTH (f);
 +      height = FRAME_PIXEL_HEIGHT (f);
 +      if (surface_set_size_func)
 +      (*surface_set_size_func) (surface, width, height);
 +
 +      unblock_input ();
 +      maybe_quit ();
 +      block_input ();
 +    }
 +
 +#ifdef CAIRO_HAS_PNG_FUNCTIONS
 +  if (surface_type == CAIRO_SURFACE_TYPE_IMAGE)
 +    {
 +      cairo_surface_flush (surface);
 +      cairo_surface_write_to_png_stream (surface, pgtk_cr_accumulate_data, &acc);
 +    }
 +#endif
 +  unblock_input ();
 +
 +  unbind_to (count, Qnil);
 +
 +  return CALLN (Fapply, intern ("concat"), Fnreverse (acc));
 +}
 +
 +
 +void
 +init_pgtkterm (void)
 +{
 +}
diff --cc src/process.c
Simple merge
diff --cc src/termhooks.h
Simple merge
diff --cc src/xdisp.c
Simple merge