summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile.in22
-rw-r--r--configure.ac106
-rw-r--r--doc/emacs/cmdargs.texi4
-rw-r--r--doc/lispref/display.texi4
-rw-r--r--doc/lispref/frames.texi2
-rw-r--r--etc/org.gnu.emacs.defaults.gschema.xml51
-rw-r--r--lib-src/emacsclient.c5
-rw-r--r--lisp/cus-edit.el10
-rw-r--r--lisp/faces.el8
-rw-r--r--lisp/frame.el55
-rw-r--r--lisp/international/mule-cmds.el2
-rw-r--r--lisp/loadup.el7
-rw-r--r--lisp/menu-bar.el2
-rw-r--r--lisp/mwheel.el8
-rw-r--r--lisp/net/browse-url.el13
-rw-r--r--lisp/net/eww.el8
-rw-r--r--lisp/select.el14
-rw-r--r--lisp/server.el17
-rw-r--r--lisp/simple.el2
-rw-r--r--lisp/startup.el2
-rw-r--r--lisp/term/pgtk-win.el504
-rw-r--r--lisp/url/url-privacy.el1
-rw-r--r--m4/gsettings.m488
-rw-r--r--src/.gdbinit8
-rw-r--r--src/Makefile.in7
-rw-r--r--src/alloc.c10
-rw-r--r--src/dispextern.h11
-rw-r--r--src/dispnew.c9
-rw-r--r--src/emacs.c14
-rw-r--r--src/emacsgtkfixed.c27
-rw-r--r--src/emacsgtkfixed.h9
-rw-r--r--src/font.c6
-rw-r--r--src/frame.c18
-rw-r--r--src/frame.h16
-rw-r--r--src/fringe.c23
-rw-r--r--src/ftcrfont.c20
-rw-r--r--src/ftfont.h1
-rw-r--r--src/gtkutil.c594
-rw-r--r--src/gtkutil.h25
-rw-r--r--src/image.c166
-rw-r--r--src/keyboard.c11
-rw-r--r--src/menu.c2
-rw-r--r--src/menu.h6
-rw-r--r--src/pgtkfns.c4159
-rw-r--r--src/pgtkgui.h119
-rw-r--r--src/pgtkim.c311
-rw-r--r--src/pgtkmenu.c1159
-rw-r--r--src/pgtkselect.c632
-rw-r--r--src/pgtkselect.h33
-rw-r--r--src/pgtkterm.c7115
-rw-r--r--src/pgtkterm.h664
-rw-r--r--src/termhooks.h12
-rw-r--r--src/terminal.c2
-rw-r--r--src/window.h2
-rw-r--r--src/xdisp.c9
-rw-r--r--src/xfaces.c24
-rw-r--r--src/xsettings.c54
-rw-r--r--src/xsettings.h15
-rw-r--r--src/xwidget.c4
60 files changed, 16080 insertions, 155 deletions
diff --git a/.gitignore b/.gitignore
index 7baee47b0a1..78557a5e876 100644
--- a/.gitignore
+++ b/.gitignore
@@ -317,3 +317,6 @@ lib-src/seccomp-filter.bpf
lib-src/seccomp-filter.pfc
lib-src/seccomp-filter-exec.bpf
lib-src/seccomp-filter-exec.pfc
+
+# gsettings schema
+/etc/*.gschema.valid
diff --git a/Makefile.in b/Makefile.in
index e6c337385a7..202665ea9d0 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -212,6 +212,9 @@ icondir=$(datarootdir)/icons
# The source directory for the icon files.
iconsrcdir=$(srcdir)/etc/images/icons
+# Where to install the gsettings schema file.
+gsettingsschemadir = @gsettingsschemadir@
+
# ==================== Emacs-specific directories ====================
# These variables hold the values Emacs will actually use. They are
@@ -306,6 +309,8 @@ LN_S_FILEONLY = @LN_S_FILEONLY@
# We use gzip to compress installed .el and some .txt files.
GZIP_PROG = @GZIP_PROG@
+GLIB_COMPILE_SCHEMAS = glib-compile-schemas
+
# ============================= Targets ==============================
# Program name transformation.
@@ -345,7 +350,9 @@ BIN_DESTDIR='${ns_appbindir}/'
ELN_DESTDIR = ${ns_applibdir}/
endif
-all: ${SUBDIR} info
+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 epaths-force-ns-self-contained etc-emacsver
@@ -501,7 +508,7 @@ $(srcdir)/configure: $(srcdir)/configure.ac $(srcdir)/m4/*.m4
## 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-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.
@@ -821,7 +828,7 @@ install-strip:
### create (but not the noninstalled files such as 'make all' would create).
###
### Don't delete the lisp and etc directories if they're in the source tree.
-uninstall: uninstall-$(NTDIR) uninstall-doc
+uninstall: uninstall-$(NTDIR) uninstall-doc uninstall-gsettings-schemas
rm -f "$(DESTDIR)$(includedir)/emacs-module.h"
$(MAKE) -C lib-src uninstall
-unset CDPATH; \
@@ -917,7 +924,7 @@ clean_dirs = $(mostlyclean_dirs) nextstep admin/charsets admin/unidata
$(foreach dir,$(clean_dirs),$(eval $(call submake_template,$(dir),clean)))
-clean: $(clean_dirs:=_clean)
+clean: $(clean_dirs:=_clean) clean-gsettings-schemas
-rm -f ./*.tmp etc/*.tmp*
-rm -rf info-dir.*
-rm -rf native-lisp
@@ -1237,3 +1244,10 @@ gitmerge:
${GITMERGE_EMACS} -batch --no-site-file --no-site-lisp \
-l ${srcdir}/admin/gitmerge.el \
--eval '(setq gitmerge-minimum-missing ${GITMERGE_NMIN})' -f gitmerge
+
+@GSETTINGS_RULES@
+
+install-gsettings-schemas:
+uninstall-gsettings-schemas:
+clean-gsettings-schemas:
+$(gsettings_SCHEMAS:.xml=.valid):
diff --git a/configure.ac b/configure.ac
index 0debc852141..9a74c52c68a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -470,6 +470,7 @@ AC_ARG_WITH([ns],[AS_HELP_STRING([--with-ns],
[use Nextstep (macOS Cocoa or GNUstep) windowing system.
On by default on macOS.])],[],[with_ns=maybe])
OPTION_DEFAULT_OFF([w32], [use native MS Windows GUI in a Cygwin build])
+OPTION_DEFAULT_OFF([pgtk], [use pure GTK build without reliance on X libs (Wayland support) (requires cairo) - Experimental])
OPTION_DEFAULT_ON([gpm],[don't use -lgpm for mouse support on a GNU/Linux console])
OPTION_DEFAULT_ON([dbus],[don't compile with D-Bus support])
@@ -1840,8 +1841,14 @@ AC_SUBST(AUTO_DEPEND)
## window-system-specific substs.
window_system=none
+
+if test "${with_pgtk}" = "yes"; then
+ window_system=pgtk
+fi
+
+
AC_PATH_X
-if test "$no_x" != yes; then
+if test "$no_x" != yes && test "${with_pgtk}" != "yes"; then
window_system=x11
fi
@@ -2304,6 +2311,11 @@ dnl use the toolkit if we have gtk, or X11R5 or newer.
w32 )
term_header=w32term.h
;;
+ pgtk )
+ term_header=pgtkterm.h
+ with_gtk3=yes
+ USE_X_TOOLKIT=none
+ ;;
haiku )
term_header=haikuterm.h
;;
@@ -2639,7 +2651,8 @@ fi
### Use -lrsvg-2 if available, unless '--with-rsvg=no' is specified.
HAVE_RSVG=no
if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" \
- || test "${opsys}" = "mingw32" || test "${HAVE_BE_APP}" = "yes"; then
+ || test "${opsys}" = "mingw32" || test "${HAVE_BE_APP}" = "yes" \
+ || test "${window_system}" = "pgtk"; then
if test "${with_rsvg}" != "no"; then
RSVG_REQUIRED=2.14.0
RSVG_MODULE="librsvg-2.0 >= $RSVG_REQUIRED"
@@ -2705,7 +2718,7 @@ fi
HAVE_IMAGEMAGICK=no
if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" || test "${HAVE_W32}" = "yes" || \
- test "${HAVE_BE_APP}" = "yes"; then
+ test "${HAVE_BE_APP}" = "yes" || test "${window_system}" = "pgtk"; then
if test "${with_imagemagick}" != "no"; then
if test -n "$BREW"; then
# Homebrew doesn't link ImageMagick 6 by default, so make sure
@@ -2788,6 +2801,9 @@ if test "${opsys}" != "mingw32"; then
AC_DEFINE([GLIB_DISABLE_DEPRECATION_WARNINGS], [1],
[Define to 1 to disable Glib deprecation warnings.])
fi
+ if test "$window_system" = pgtk; then
+ GLIB_GSETTINGS
+ fi
else
check_gtk2=yes
gtk3_pkg_errors="$GTK_PKG_ERRORS "
@@ -2929,6 +2945,17 @@ AC_SUBST(XWIDGETS_OBJ)
CFLAGS=$OLD_CFLAGS
LIBS=$OLD_LIBS
+PGTK_OBJ=
+PGTK_LIBS=
+if test "$window_system" = "pgtk"; then
+ PGTK_OBJ="pgtkfns.o pgtkterm.o pgtkselect.o pgtkmenu.o pgtkim.o xsettings.o"
+ PGTK_LIBS="$GTK_LIBS"
+ HAVE_PGTK=yes
+ AC_DEFINE([HAVE_PGTK], 1, [Define to 1 if you have pure Gtk+-3.])
+fi
+AC_SUBST(PGTK_OBJ)
+AC_SUBST(PGTK_LIBS)
+
dnl D-Bus has been tested under GNU/Linux only. Must be adapted for
dnl other platforms.
HAVE_DBUS=no
@@ -2958,7 +2985,7 @@ AC_SUBST(DBUS_OBJ)
dnl GSettings has been tested under GNU/Linux only.
HAVE_GSETTINGS=no
-if test "${HAVE_X11}" = "yes" && test "${with_gsettings}" = "yes"; then
+if test "${HAVE_X11}" = "yes" -o "${window_system}" = "pgtk" && test "${with_gsettings}" = "yes"; then
EMACS_CHECK_MODULES([GSETTINGS], [gio-2.0 >= 2.26])
if test "$HAVE_GSETTINGS" = "yes"; then
old_CFLAGS=$CFLAGS
@@ -2992,7 +3019,7 @@ fi
dnl GConf has been tested under GNU/Linux only.
dnl The version is really arbitrary, it is about the same age as Gtk+ 2.6.
HAVE_GCONF=no
-if test "${HAVE_X11}" = "yes" && test "${with_gconf}" != "no"; then
+if test "${HAVE_X11}" = "yes" -o "${window_system}" = "pgtk" && test "${with_gconf}" != "no"; then
EMACS_CHECK_MODULES([GCONF], [gconf-2.0 >= 2.13])
if test "$HAVE_GCONF" = yes; then
AC_DEFINE(HAVE_GCONF, 1, [Define to 1 if using GConf.])
@@ -3583,10 +3610,34 @@ if test "${HAVE_X11}" = "yes"; then
fi
fi
else # "${HAVE_X11}" != "yes"
- HAVE_XFT=no
- HAVE_FREETYPE=no
- HAVE_LIBOTF=no
- HAVE_M17N_FLT=no
+ if test $window_system = pgtk; then
+ EMACS_CHECK_MODULES([FONTCONFIG], [fontconfig >= 2.2.0])
+ EMACS_CHECK_MODULES([FREETYPE], [freetype2])
+ if test "$HAVE_FONTCONFIG" != yes -o "$HAVE_FREETYPE" != yes; then
+ AC_MSG_ERROR(fontconfig and freetype is required.)
+ fi
+ HAVE_LIBOTF=no
+ AC_DEFINE(HAVE_FREETYPE, 1,
+ [Define to 1 if using the freetype and fontconfig libraries.])
+ if test "${with_libotf}" != "no"; then
+ EMACS_CHECK_MODULES([LIBOTF], [libotf])
+ if test "$HAVE_LIBOTF" = "yes"; then
+ AC_DEFINE(HAVE_LIBOTF, 1, [Define to 1 if using libotf.])
+ AC_CHECK_LIB(otf, OTF_get_variation_glyphs,
+ HAVE_OTF_GET_VARIATION_GLYPHS=yes,
+ HAVE_OTF_GET_VARIATION_GLYPHS=no)
+ if test "${HAVE_OTF_GET_VARIATION_GLYPHS}" = "yes"; then
+ AC_DEFINE(HAVE_OTF_GET_VARIATION_GLYPHS, 1,
+ [Define to 1 if libotf has OTF_get_variation_glyphs.])
+ fi
+ fi
+ fi
+ else
+ HAVE_XFT=no
+ HAVE_FREETYPE=no
+ HAVE_LIBOTF=no
+ HAVE_M17N_FLT=no
+ fi
fi # "${HAVE_X11}" != "yes"
HAVE_HARFBUZZ=no
@@ -3598,6 +3649,7 @@ else
harfbuzz_required_ver=0.9.42
fi
if test "${HAVE_X11}" = "yes" && test "${HAVE_FREETYPE}" = "yes" \
+ || test "$window_system" = "pgtk" \
|| test "${HAVE_W32}" = "yes"; then
if test "${with_harfbuzz}" != "no"; then
EMACS_CHECK_MODULES([HARFBUZZ], [harfbuzz >= $harfbuzz_required_ver])
@@ -3677,6 +3729,25 @@ AC_SUBST(LIBOTF_LIBS)
AC_SUBST(M17N_FLT_CFLAGS)
AC_SUBST(M17N_FLT_LIBS)
+HAVE_CAIRO=no
+if test "${HAVE_X11}" = "yes" -o "$window_system" = pgtk; then
+ if test "${with_cairo}" != "no"; then
+ CAIRO_REQUIRED=1.12.0
+ CAIRO_MODULE="cairo >= $CAIRO_REQUIRED"
+ EMACS_CHECK_MODULES(CAIRO, $CAIRO_MODULE)
+ if test $HAVE_CAIRO = yes; then
+ AC_DEFINE(USE_CAIRO, 1, [Define to 1 if using cairo.])
+ else
+ AC_MSG_ERROR([cairo requested but not found.])
+ fi
+
+ CFLAGS="$CFLAGS $CAIRO_CFLAGS"
+ LIBS="$LIBS $CAIRO_LIBS"
+ AC_SUBST(CAIRO_CFLAGS)
+ AC_SUBST(CAIRO_LIBS)
+ fi
+fi
+
if test "${HAVE_X11}" = "yes"; then
AC_CHECK_HEADER(X11/Xlib-xcb.h,
AC_CHECK_LIB(xcb, xcb_translate_coordinates, HAVE_XCB=yes))
@@ -3785,7 +3856,8 @@ AC_SUBST(LIBXPM)
HAVE_JPEG=no
LIBJPEG=
if test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
- || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes"; then
+ || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes" \
+ || test "$window_system" = "pgtk"; then
if test "${with_jpeg}" != "no"; then
AC_CACHE_CHECK([for jpeglib 6b or later],
[emacs_cv_jpeglib],
@@ -4103,7 +4175,8 @@ if test "${with_png}" != no; then
if test "$opsys" = mingw32; then
AC_CHECK_HEADER([png.h], [HAVE_PNG=yes])
elif test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
- || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes"; then
+ || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes" \
+ || test "$window_system" = "pgtk"; then
EMACS_CHECK_MODULES([PNG], [libpng >= 1.0.0])
if test $HAVE_PNG = yes; then
LIBPNG=$PNG_LIBS
@@ -4178,7 +4251,8 @@ if test "${opsys}" = "mingw32"; then
AC_DEFINE(HAVE_TIFF, 1, [Define to 1 if you have the tiff library (-ltiff).])
fi
elif test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
- || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes"; then
+ || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes" \
+ || test "$window_system" = "pgtk"; then
if test "${with_tiff}" != "no"; then
AC_CHECK_HEADER(tiffio.h,
[tifflibs="-lz -lm"
@@ -4208,7 +4282,7 @@ if test "${opsys}" = "mingw32"; then
fi
elif test "${HAVE_X11}" = "yes" && test "${with_gif}" != "no" \
|| test "${HAVE_W32}" = "yes" || test "${HAVE_NS}" = "yes" \
- || test "${HAVE_BE_APP}" = "yes"; then
+ || test "${HAVE_BE_APP}" = "yes" || test "$window_system" = "pgtk"; then
AC_CHECK_HEADER(gif_lib.h,
# EGifPutExtensionLast only exists from version libungif-4.1.0b1.
# Earlier versions can crash Emacs, but version 5.0 removes EGifPutExtensionLast.
@@ -5713,6 +5787,7 @@ AC_SUBST(prefix)
AC_SUBST(exec_prefix)
AC_SUBST(bindir)
AC_SUBST(datadir)
+AC_SUBST(gsettingsschemadir)
AC_SUBST(sharedstatedir)
AC_SUBST(libexecdir)
AC_SUBST(mandir)
@@ -5775,6 +5850,9 @@ if test "${HAVE_X_WINDOWS}" = "yes" ; then
FONT_OBJ="$FONT_OBJ ftfont.o"
fi
fi
+if test "${window_system}" = "pgtk"; then
+ FONT_OBJ="ftfont.o ftcrfont.o"
+fi
if test "${HAVE_BE_APP}" = "yes" ; then
if test "${HAVE_FREETYPE}" = "yes" || \
@@ -6176,7 +6254,7 @@ optsep=
emacs_config_features=
for opt in ACL BE_APP CAIRO DBUS FREETYPE GCONF GIF GLIB GMP GNUTLS GPM GSETTINGS \
HARFBUZZ IMAGEMAGICK JPEG JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 \
- 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 SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS \
UNEXEC WEBP X11 XAW3D XDBE XFT XIM XINPUT2 XPM XWIDGETS X_TOOLKIT \
ZLIB; do
diff --git a/doc/emacs/cmdargs.texi b/doc/emacs/cmdargs.texi
index 86c04c84a2a..b1e471f6d63 100644
--- a/doc/emacs/cmdargs.texi
+++ b/doc/emacs/cmdargs.texi
@@ -756,6 +756,10 @@ On MS-Windows, if you set this variable, Emacs will load and initialize
the network library at startup, instead of waiting until the first
time it is required.
+@item WAYLAND_DISPLAY
+Pgtk Emacs (built with @option{--with-pgtk}) can run on Wayland natively.
+@env{WAYLAND_DISPLAY} specifies the connection to the compositor.
+
@item emacs_dir
On MS-Windows, @env{emacs_dir} is a special environment variable, which
indicates the full path of the directory in which Emacs is installed.
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 1460e070a0a..98a15404f91 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -2791,8 +2791,8 @@ apply to. Here are the possible values of @var{characteristic}:
The kind of window system the terminal uses---either @code{graphic}
(any graphics-capable display), @code{x}, @code{pc} (for the MS-DOS
console), @code{w32} (for MS Windows 9X/NT/2K/XP), @code{haiku} (for
-Haiku), or @code{tty} (a non-graphics-capable display).
-@xref{Window Systems, window-system}.
+Haiku), @code{pgtk} (for GTK), or @code{tty} (a non-graphics-capable
+display). @xref{Window Systems, window-system}.
@item class
What kinds of colors the terminal supports---either @code{color},
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 00332fed0bf..712c842a048 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -216,7 +216,7 @@ The terminal and keyboard coding systems used on the terminal.
@item
The kind of display associated with the terminal. This is the symbol
returned by the function @code{terminal-live-p} (i.e., @code{x},
-@code{t}, @code{w32}, @code{ns}, @code{pc}, or @code{haiku}).
+@code{t}, @code{w32}, @code{ns}, @code{pc}, @code{haiku}, or @code{pgtk}).
@xref{Frames}.
@item
diff --git a/etc/org.gnu.emacs.defaults.gschema.xml b/etc/org.gnu.emacs.defaults.gschema.xml
new file mode 100644
index 00000000000..7c700ac0b65
--- /dev/null
+++ b/etc/org.gnu.emacs.defaults.gschema.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019-2020 Free Software Foundation, Inc. -->
+<schemalist>
+
+ <schema id="org.gnu.emacs.defaults">
+
+ <key name='alpha' type='s'><default>''</default></key>
+ <key name='auto-raise-lower' type='s'><default>''</default></key>
+ <key name='auto-lower' type='s'><default>''</default></key>
+ <key name='auto-raise' type='s'><default>''</default></key>
+ <key name='background' type='s'><default>''</default></key>
+ <key name='background-mode' type='s'><default>''</default></key>
+ <key name='bitmap-icon' type='s'><default>''</default></key>
+ <key name='border-color' type='s'><default>''</default></key>
+ <key name='border-width' type='s'><default>''</default></key>
+ <key name='buffer-predicate' type='s'><default>''</default></key>
+ <key name='cursor-blink' type='s'><default>''</default></key>
+ <key name='cursor-type' type='s'><default>''</default></key>
+ <key name='cursor-color' type='s'><default>''</default></key>
+ <key name='font' type='s'><default>''</default></key>
+ <key name='font-backend' type='s'><default>''</default></key>
+ <key name='foreground' type='s'><default>''</default></key>
+ <key name='fullscreen' type='s'><default>''</default></key>
+ <key name='horizontal-scroll-bars' type='s'><default>''</default></key>
+ <key name='icon-name' type='s'><default>''</default></key>
+ <key name='inhibit-double-buffering' type='s'><default>''</default></key>
+ <key name='internal-border' type='s'><default>''</default></key>
+ <key name='internal-border-width' type='s'><default>''</default></key>
+ <key name='left-fringe' type='s'><default>''</default></key>
+ <key name='line-spacing' type='s'><default>''</default></key>
+ <key name='menu-bar' type='s'><default>''</default></key>
+ <key name='minibuffer' type='s'><default>''</default></key>
+ <key name='name' type='s'><default>''</default></key>
+ <key name='pointer-color' type='s'><default>''</default></key>
+ <key name='reverse-video' type='s'><default>''</default></key>
+ <key name='right-fringe' type='s'><default>''</default></key>
+ <key name='screen-gamma' type='s'><default>''</default></key>
+ <key name='scroll-bar' type='s'><default>''</default></key>
+ <key name='scroll-bar-background' type='s'><default>''</default></key>
+ <key name='scroll-bar-foreground' type='s'><default>''</default></key>
+ <key name='scroll-bar-height' type='s'><default>''</default></key>
+ <key name='scroll-bar-width' type='s'><default>''</default></key>
+ <key name='scroll-bars' type='s'><default>''</default></key>
+ <key name='title' type='s'><default>''</default></key>
+ <key name='tool-bar' type='s'><default>''</default></key>
+ <key name='vertical-scroll-bars' type='s'><default>''</default></key>
+ <key name='wait-for-w-m' type='s'><default>''</default></key>
+
+ </schema>
+
+</schemalist>
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index ea9f72dfae0..7769e015edc 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -607,7 +607,12 @@ decode_options (int argc, char **argv)
alt_display = "be";
#endif
+#ifdef HAVE_PGTK
+ display = egetenv ("WAYLAND_DISPLAY");
+ alt_display = egetenv ("DISPLAY");
+#else
display = egetenv ("DISPLAY");
+#endif
}
if (!display)
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index b7c53a4dfed..ae71140e262 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -2176,7 +2176,7 @@ and `face'."
;;; The `custom' Widget.
(defface custom-button
- '((((type x w32 ns haiku) (class color)) ; Like default mode line
+ '((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "lightgrey" :foreground "black"))
"Face for custom buffer buttons if `custom-raised-buttons' is non-nil."
@@ -2184,7 +2184,7 @@ and `face'."
:group 'custom-faces)
(defface custom-button-mouse
- '((((type x w32 ns haiku) (class color))
+ '((((type x w32 ns haiku pgtk) (class color))
:box (:line-width 2 :style released-button)
:background "grey90" :foreground "black")
(t
@@ -2209,7 +2209,7 @@ and `face'."
(if custom-raised-buttons 'custom-button-mouse 'highlight))
(defface custom-button-pressed
- '((((type x w32 ns haiku) (class color))
+ '((((type x w32 ns haiku pgtk) (class color))
:box (:line-width 2 :style pressed-button)
:background "lightgrey" :foreground "black")
(t :inverse-video t))
@@ -3458,6 +3458,10 @@ MS Windows.")
:sibling-args (:help-echo "\
GNUstep or Macintosh OS Cocoa interface.")
ns)
+ (const :format "PGTK "
+ :sibling-args (:help-echo "\
+Pure-GTK interface.")
+ ns)
(const :format "DOS "
:sibling-args (:help-echo "\
Plain MS-DOS.")
diff --git a/lisp/faces.el b/lisp/faces.el
index 9caba3a11be..97579877efa 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -1172,13 +1172,13 @@ an integer value."
(:height
'integerp)
(:stipple
- (and (memq (window-system frame) '(x ns)) ; No stipple on w32 or haiku
+ (and (memq (window-system frame) '(x ns pgtk)) ; No stipple on w32 or haiku
(mapcar #'list
(apply #'nconc
(mapcar (lambda (dir)
(and (file-readable-p dir)
(file-directory-p dir)
- (directory-files dir)))
+ (directory-files dir 'full)))
x-bitmap-file-path)))))
(:inherit
(cons '("none" . nil)
@@ -1516,7 +1516,7 @@ If FRAME is nil, the current FRAME is used."
match (cond ((eq req 'type)
(or (memq (window-system frame) options)
(and (memq 'graphic options)
- (memq (window-system frame) '(x w32 ns)))
+ (memq (window-system frame) '(x w32 ns pgtk)))
;; FIXME: This should be revisited to use
;; display-graphic-p, provided that the
;; color selection depends on the number
@@ -2840,7 +2840,7 @@ Note: Other faces cannot inherit from the cursor face."
'((default
:box (:line-width 1 :style released-button)
:foreground "black")
- (((type x w32 ns haiku) (class color))
+ (((type x w32 ns haiku pgtk) (class color))
:background "grey75")
(((type x) (class mono))
:background "grey"))
diff --git a/lisp/frame.el b/lisp/frame.el
index dc7c628e81e..13929047d08 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -1637,6 +1637,7 @@ live frame and defaults to the selected one."
(declare-function x-frame-geometry "xfns.c" (&optional frame))
(declare-function w32-frame-geometry "w32fns.c" (&optional frame))
(declare-function ns-frame-geometry "nsfns.m" (&optional frame))
+(declare-function pgtk-frame-geometry "pgtkfns.c" (&optional frame))
(declare-function haiku-frame-geometry "haikufns.c" (&optional frame))
(defun frame-geometry (&optional frame)
@@ -1687,6 +1688,8 @@ and width values are in pixels.
(w32-frame-geometry frame))
((eq frame-type 'ns)
(ns-frame-geometry frame))
+ ((eq frame-type 'pgtk)
+ (pgtk-frame-geometry frame))
((eq frame-type 'haiku)
(haiku-frame-geometry frame))
(t
@@ -1813,6 +1816,7 @@ of frames like calls to map a frame or change its visibility."
(declare-function x-frame-edges "xfns.c" (&optional frame type))
(declare-function w32-frame-edges "w32fns.c" (&optional frame type))
(declare-function ns-frame-edges "nsfns.m" (&optional frame type))
+(declare-function pgtk-frame-edges "pgtkfns.c" (&optional frame type))
(declare-function haiku-frame-edges "haikufns.c" (&optional frame type))
(defun frame-edges (&optional frame type)
@@ -1837,6 +1841,8 @@ FRAME."
(w32-frame-edges frame type))
((eq frame-type 'ns)
(ns-frame-edges frame type))
+ ((eq frame-type 'pgtk)
+ (pgtk-frame-edges frame type))
((eq frame-type 'haiku)
(haiku-frame-edges frame type))
(t
@@ -1845,6 +1851,7 @@ FRAME."
(declare-function w32-mouse-absolute-pixel-position "w32fns.c")
(declare-function x-mouse-absolute-pixel-position "xfns.c")
(declare-function ns-mouse-absolute-pixel-position "nsfns.m")
+(declare-function pgtk-mouse-absolute-pixel-position "pgtkfns.c")
(declare-function haiku-mouse-absolute-pixel-position "haikufns.c")
(defun mouse-absolute-pixel-position ()
@@ -1860,11 +1867,14 @@ position (0, 0) of the selected frame's terminal."
(w32-mouse-absolute-pixel-position))
((eq frame-type 'ns)
(ns-mouse-absolute-pixel-position))
+ ((eq frame-type 'pgtk)
+ (pgtk-mouse-absolute-pixel-position))
((eq frame-type 'haiku)
(haiku-mouse-absolute-pixel-position))
(t
(cons 0 0)))))
+(declare-function pgtk-set-mouse-absolute-pixel-position "pgtkfns.c" (x y))
(declare-function ns-set-mouse-absolute-pixel-position "nsfns.m" (x y))
(declare-function w32-set-mouse-absolute-pixel-position "w32fns.c" (x y))
(declare-function x-set-mouse-absolute-pixel-position "xfns.c" (x y))
@@ -1876,6 +1886,8 @@ The coordinates X and Y are interpreted in pixels relative to a
position (0, 0) of the selected frame's terminal."
(let ((frame-type (framep-on-display)))
(cond
+ ((eq frame-type 'pgtk)
+ (pgtk-set-mouse-absolute-pixel-position x y))
((eq frame-type 'ns)
(ns-set-mouse-absolute-pixel-position x y))
((eq frame-type 'x)
@@ -1976,6 +1988,7 @@ workarea attribute."
(declare-function x-frame-list-z-order "xfns.c" (&optional display))
(declare-function w32-frame-list-z-order "w32fns.c" (&optional display))
(declare-function ns-frame-list-z-order "nsfns.m" (&optional display))
+(declare-function pgtk-frame-list-z-order "pgtkfns.c" (&optional display))
(declare-function haiku-frame-list-z-order "haikufns.c" (&optional display))
(defun frame-list-z-order (&optional display)
@@ -1997,12 +2010,15 @@ Return nil if DISPLAY contains no Emacs frame."
(w32-frame-list-z-order display))
((eq frame-type 'ns)
(ns-frame-list-z-order display))
+ ((eq frame-type 'pgtk)
+ (pgtk-frame-list-z-order display))
((eq frame-type 'haiku)
(haiku-frame-list-z-order display)))))
(declare-function x-frame-restack "xfns.c" (frame1 frame2 &optional above))
(declare-function w32-frame-restack "w32fns.c" (frame1 frame2 &optional above))
(declare-function ns-frame-restack "nsfns.m" (frame1 frame2 &optional above))
+(declare-function pgtk-frame-restack "pgtkfns.c" (frame1 frame2 &optional above))
(defun frame-restack (frame1 frame2 &optional above)
"Restack FRAME1 below FRAME2.
@@ -2032,7 +2048,9 @@ Some window managers may refuse to restack windows."
((eq frame-type 'w32)
(w32-frame-restack frame1 frame2 above))
((eq frame-type 'ns)
- (ns-frame-restack frame1 frame2 above))))
+ (ns-frame-restack frame1 frame2 above))
+ ((eq frame-type 'pgtk)
+ (pgtk-frame-restack frame1 frame2 above))))
(error "Cannot restack frames")))
(defun frame-size-changed-p (&optional frame)
@@ -2079,8 +2097,8 @@ frame's display)."
((eq frame-type 'w32)
(with-no-warnings
(> w32-num-mouse-buttons 0)))
- ((memq frame-type '(x ns haiku))
- t) ;; We assume X, NeXTstep and Haiku *always* have a pointing device
+ ((memq frame-type '(x ns haiku pgtk))
+ t) ;; We assume X, NeXTstep, GTK, and Haiku *always* have a pointing device
(t
(or (and (featurep 'xt-mouse)
xterm-mouse-mode)
@@ -2105,7 +2123,7 @@ frames and several different fonts at once. This is true for displays
that use a window system such as X, and false for text-only terminals.
DISPLAY can be a display name, a frame, or nil (meaning the selected
frame's display)."
- (not (null (memq (framep-on-display display) '(x w32 ns haiku)))))
+ (not (null (memq (framep-on-display display) '(x w32 ns pgtk haiku)))))
(defun display-images-p (&optional display)
"Return non-nil if DISPLAY can display images.
@@ -2133,7 +2151,7 @@ frame's display)."
;; a Windows DOS Box.
(with-no-warnings
(not (null dos-windows-version))))
- ((memq frame-type '(x w32 ns))
+ ((memq frame-type '(x w32 ns pgtk))
t)
(t
nil))))
@@ -2143,7 +2161,7 @@ frame's display)."
This means that, for example, DISPLAY can differentiate between
the keybinding RET and [return]."
(let ((frame-type (framep-on-display display)))
- (or (memq frame-type '(x w32 ns pc))
+ (or (memq frame-type '(x w32 ns pc pgtk))
;; MS-DOS and MS-Windows terminals have built-in support for
;; function (symbol) keys
(memq system-type '(ms-dos windows-nt)))))
@@ -2156,7 +2174,7 @@ DISPLAY should be either a frame or a display name (a string).
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku))
+ ((memq frame-type '(x w32 ns haiku pgtk))
(x-display-screens display))
(t
1))))
@@ -2176,7 +2194,7 @@ with DISPLAY. To get information for each physical monitor, use
`display-monitor-attributes-list'."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku))
+ ((memq frame-type '(x w32 ns haiku pgtk))
(x-display-pixel-height display))
(t
(frame-height (if (framep display) display (selected-frame)))))))
@@ -2196,7 +2214,7 @@ with DISPLAY. To get information for each physical monitor, use
`display-monitor-attributes-list'."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku))
+ ((memq frame-type '(x w32 ns haiku pgtk))
(x-display-pixel-width display))
(t
(frame-width (if (framep display) display (selected-frame)))))))
@@ -2234,7 +2252,7 @@ For graphical terminals, note that on \"multi-monitor\" setups this
refers to the height in millimeters for all physical monitors
associated with DISPLAY. To get information for each physical
monitor, use `display-monitor-attributes-list'."
- (and (memq (framep-on-display display) '(x w32 ns haiku))
+ (and (memq (framep-on-display display) '(x w32 ns haiku pgtk))
(or (cddr (assoc (or display (frame-parameter nil 'display))
display-mm-dimensions-alist))
(cddr (assoc t display-mm-dimensions-alist))
@@ -2255,7 +2273,7 @@ For graphical terminals, note that on \"multi-monitor\" setups this
refers to the width in millimeters for all physical monitors
associated with DISPLAY. To get information for each physical
monitor, use `display-monitor-attributes-list'."
- (and (memq (framep-on-display display) '(x w32 ns haiku))
+ (and (memq (framep-on-display display) '(x w32 ns haiku pgtk))
(or (cadr (assoc (or display (frame-parameter nil 'display))
display-mm-dimensions-alist))
(cadr (assoc t display-mm-dimensions-alist))
@@ -2273,7 +2291,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku))
+ ((memq frame-type '(x w32 ns haiku pgtk))
(x-display-backing-store display))
(t
'not-useful))))
@@ -2286,7 +2304,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku))
+ ((memq frame-type '(x w32 ns haiku pgtk))
(x-display-save-under display))
(t
'not-useful))))
@@ -2299,7 +2317,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku))
+ ((memq frame-type '(x w32 ns haiku pgtk))
(x-display-planes display))
((eq frame-type 'pc)
4)
@@ -2314,7 +2332,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku))
+ ((memq frame-type '(x w32 ns haiku pgtk))
(x-display-color-cells display))
((eq frame-type 'pc)
16)
@@ -2331,7 +2349,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku))
+ ((memq frame-type '(x w32 ns haiku pgtk))
(x-display-visual-class display))
((and (memq frame-type '(pc t))
(tty-display-color-p display))
@@ -2345,6 +2363,8 @@ If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(&optional display))
(declare-function ns-display-monitor-attributes-list "nsfns.m"
(&optional terminal))
+(declare-function pgtk-display-monitor-attributes-list "pgtkfns.c"
+ (&optional terminal))
(defun display-monitor-attributes-list (&optional display)
"Return a list of physical monitor attributes on DISPLAY.
@@ -2362,6 +2382,7 @@ of attribute keys and values as follows:
mm-size -- Width and height in millimeters in the form of
(WIDTH HEIGHT)
frames -- List of frames dominated by the physical monitor
+ scale-factor (*) -- Scale factor (float)
name (*) -- Name of the physical monitor as a string
source (*) -- Source of multi-monitor information as a string
@@ -2393,6 +2414,8 @@ monitors."
(w32-display-monitor-attributes-list display))
((eq frame-type 'ns)
(ns-display-monitor-attributes-list display))
+ ((eq frame-type 'pgtk)
+ (pgtk-display-monitor-attributes-list display))
(t
(let ((geometry (list 0 0 (display-pixel-width display)
(display-pixel-height display))))
diff --git a/lisp/international/mule-cmds.el b/lisp/international/mule-cmds.el
index b84d9e0823d..7f9b14bdfd9 100644
--- a/lisp/international/mule-cmds.el
+++ b/lisp/international/mule-cmds.el
@@ -88,7 +88,7 @@
(bindings--define-key map [separator-3] menu-bar-separator)
(bindings--define-key map [set-terminal-coding-system]
'(menu-item "For Terminal" set-terminal-coding-system
- :enable (null (memq initial-window-system '(x w32 ns haiku)))
+ :enable (null (memq initial-window-system '(x w32 ns haiku pgtk)))
:help "How to encode terminal output"))
(bindings--define-key map [set-keyboard-coding-system]
'(menu-item "For Keyboard" set-keyboard-coding-system
diff --git a/lisp/loadup.el b/lisp/loadup.el
index 71d6a501b94..b87c0550fc5 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -340,6 +340,13 @@
(load "international/mule-util")
(load "international/ucs-normalize")
(load "term/ns-win"))))
+(if (featurep 'pgtk)
+ (progn
+ (load "term/common-win")
+ ;; Don't load ucs-normalize.el unless uni-*.el files were
+ ;; already produced, because it needs uni-*.el files that might
+ ;; not be built early enough during bootstrap.
+ (load "term/pgtk-win")))
(if (fboundp 'x-create-frame)
;; Do it after loading term/foo-win.el since the value of the
;; mouse-wheel-*-event vars depends on those files being loaded or not.
diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el
index 8c04e35a51f..bd110226618 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -2539,6 +2539,7 @@ See `menu-bar-mode' for more information."
(declare-function x-menu-bar-open "term/x-win" (&optional frame))
(declare-function w32-menu-bar-open "term/w32-win" (&optional frame))
+(declare-function pgtk-menu-bar-open "term/pgtk-win" (&optional frame))
(declare-function haiku-menu-bar-open "haikumenu.c" (&optional frame))
(defun lookup-key-ignore-too-long (map key)
@@ -2678,6 +2679,7 @@ If FRAME is nil or not given, use the selected frame."
((eq type 'x) (x-menu-bar-open frame))
((eq type 'w32) (w32-menu-bar-open frame))
((eq type 'haiku) (haiku-menu-bar-open frame))
+ ((eq type 'pgtk) (pgtk-menu-bar-open frame))
((and (null tty-menu-open-use-tmm)
(not (zerop (or (frame-parameter nil 'menu-bar-lines) 0))))
;; Make sure the menu bar is up to date. One situation where
diff --git a/lisp/mwheel.el b/lisp/mwheel.el
index 9d303b7ee35..fbe8daa77f8 100644
--- a/lisp/mwheel.el
+++ b/lisp/mwheel.el
@@ -56,7 +56,7 @@
(defcustom mouse-wheel-down-event
(if (or (featurep 'w32-win) (featurep 'ns-win)
- (featurep 'haiku-win))
+ (featurep 'haiku-win) (featurep 'pgtk-win))
'wheel-up
'mouse-4)
"Event used for scrolling down."
@@ -77,7 +77,7 @@
(defcustom mouse-wheel-up-event
(if (or (featurep 'w32-win) (featurep 'ns-win)
- (featurep 'haiku-win))
+ (featurep 'haiku-win) (featurep 'pgtk-win))
'wheel-down
'mouse-5)
"Event used for scrolling up."
@@ -246,7 +246,7 @@ Also see `mouse-wheel-tilt-scroll'."
(defvar mouse-wheel-left-event
(if (or (featurep 'w32-win) (featurep 'ns-win)
- (featurep 'haiku-win))
+ (featurep 'haiku-win) (featurep 'pgtk-win))
'wheel-left
'mouse-6)
"Event used for scrolling left.")
@@ -260,7 +260,7 @@ Also see `mouse-wheel-tilt-scroll'."
(defvar mouse-wheel-right-event
(if (or (featurep 'w32-win) (featurep 'ns-win)
- (featurep 'haiku-win))
+ (featurep 'haiku-win) (featurep 'pgtk-win))
'wheel-right
'mouse-7)
"Event used for scrolling right.")
diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index 2be9c7c025c..0273478cacf 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -824,8 +824,17 @@ If ARGS are omitted, the default is to pass
;; When connected to various displays, be careful to use the display of
;; the currently selected frame, rather than the original start display,
;; which may not even exist any more.
- (if (stringp (frame-parameter nil 'display))
- (setenv "DISPLAY" (frame-parameter nil 'display)))
+ (let ((dpy (frame-parameter nil 'display))
+ classname)
+ (if (stringp dpy)
+ (cond
+ ((featurep 'pgtk)
+ (setq classname (pgtk-backend-display-class))
+ (if (equal classname "GdkWaylandDisplay")
+ (setenv "WAYLAND_DISPLAY" dpy)
+ (setenv "DISPLAY" dpy)))
+ (t
+ (setenv "DISPLAY" dpy)))))
(if (functionp function)
(apply function url args)
(error "No suitable browser for URL %s" url))))
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index e86d21f889c..0c66cf3a0d7 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -239,7 +239,7 @@ parameter, and should return the (possibly) transformed URL."
:version "29.1")
(defface eww-form-submit
- '((((type x w32 ns haiku) (class color)) ; Like default mode line
+ '((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "#808080" :foreground "black"))
"Face for eww buffer buttons."
@@ -247,7 +247,7 @@ parameter, and should return the (possibly) transformed URL."
:group 'eww)
(defface eww-form-file
- '((((type x w32 ns haiku) (class color)) ; Like default mode line
+ '((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "#808080" :foreground "black"))
"Face for eww buffer buttons."
@@ -255,7 +255,7 @@ parameter, and should return the (possibly) transformed URL."
:group 'eww)
(defface eww-form-checkbox
- '((((type x w32 ns haiku) (class color)) ; Like default mode line
+ '((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "lightgrey" :foreground "black"))
"Face for eww buffer buttons."
@@ -263,7 +263,7 @@ parameter, and should return the (possibly) transformed URL."
:group 'eww)
(defface eww-form-select
- '((((type x w32 ns haiku) (class color)) ; Like default mode line
+ '((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "lightgrey" :foreground "black"))
"Face for eww buffer buttons."
diff --git a/lisp/select.el b/lisp/select.el
index 43424d94b39..5e7f4a696a3 100644
--- a/lisp/select.el
+++ b/lisp/select.el
@@ -140,24 +140,27 @@ MS-Windows does not have a \"primary\" selection."
(defcustom x-select-request-type nil
"Data type request for X selection.
The value is one of the following data types, a list of them, or nil:
- `COMPOUND_TEXT', `UTF8_STRING', `STRING', `TEXT'
+ `COMPOUND_TEXT', `UTF8_STRING', `STRING', `TEXT', `text/plain\\;charset=utf-8'
If the value is one of the above symbols, try only the specified type.
If the value is a list of them, try each of them in the specified
order until succeed.
-The value nil is the same as the list (UTF8_STRING COMPOUND_TEXT STRING)."
+The value nil is the same as the list (UTF8_STRING COMPOUND_TEXT STRING
+text/plain\\;charset=utf-8)."
:type '(choice (const :tag "Default" nil)
(const COMPOUND_TEXT)
(const UTF8_STRING)
(const STRING)
(const TEXT)
+ (const text/plain\;charset=utf-8)
(set :tag "List of values"
(const COMPOUND_TEXT)
(const UTF8_STRING)
(const STRING)
- (const TEXT)))
+ (const TEXT)
+ (const text/plain\;charset=utf-8)))
:group 'killing)
(defun gui--selection-value-internal (type)
@@ -165,9 +168,9 @@ The value nil is the same as the list (UTF8_STRING COMPOUND_TEXT STRING)."
Call `gui-get-selection' with an appropriate DATA-TYPE argument
decided by `x-select-request-type'. The return value is already
decoded. If `gui-get-selection' signals an error, return nil."
- (let ((request-type (if (eq window-system 'x)
+ (let ((request-type (if (memq window-system '(x pgtk))
(or x-select-request-type
- '(UTF8_STRING COMPOUND_TEXT STRING))
+ '(UTF8_STRING COMPOUND_TEXT STRING text/plain\;charset=utf-8))
'STRING))
text)
(with-demoted-errors "gui-get-selection: %S"
@@ -312,6 +315,7 @@ the formats available in the clipboard if TYPE is `CLIPBOARD'."
selection-coding-system
(pcase data-type
('UTF8_STRING 'utf-8)
+ ('text/plain\;charset=utf-8 'utf-8)
('COMPOUND_TEXT 'compound-text-with-extensions)
('C_STRING nil)
('STRING 'iso-8859-1)))))
diff --git a/lisp/server.el b/lisp/server.el
index 2f003a380a1..d510df1208a 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -900,12 +900,17 @@ This handles splitting the command if it would be bigger than
)
(cond (w
- (server--create-frame
- nowait proc
- `((display . ,display)
- ,@(if parent-id
- `((parent-id . ,(string-to-number parent-id))))
- ,@parameters)))
+ (condition-case nil
+ (server--create-frame
+ nowait proc
+ `((display . ,display)
+ ,@(if parent-id
+ `((parent-id . ,(string-to-number parent-id))))
+ ,@parameters))
+ (error
+ (server-log "Window system unsupported" proc)
+ (server-send-string proc "-window-system-unsupported \n")
+ nil)))
(t
(server-log "Window system unsupported" proc)
diff --git a/lisp/simple.el b/lisp/simple.el
index 84928caa310..26c3ff575e2 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -9616,7 +9616,7 @@ call `normal-erase-is-backspace-mode' (which see) instead."
(if (if (eq normal-erase-is-backspace 'maybe)
(and (not noninteractive)
(or (memq system-type '(ms-dos windows-nt))
- (memq window-system '(w32 ns))
+ (memq window-system '(w32 ns pgtk))
(and (eq window-system 'x)
(fboundp 'x-backspace-delete-keys-p)
(x-backspace-delete-keys-p))
diff --git a/lisp/startup.el b/lisp/startup.el
index a46a3a0233d..b79467339b2 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -2020,6 +2020,8 @@ we put it on this frame."
;; frame visible.
(if (eq (window-system) 'w32)
(sit-for 0 t))
+ (if (eq (window-system) 'pgtk)
+ (sit-for 0.1 t))
(dolist (frame (append (frame-list) (list (selected-frame))))
(if (and (frame-visible-p frame)
(not (window-minibuffer-p (frame-selected-window frame))))
diff --git a/lisp/term/pgtk-win.el b/lisp/term/pgtk-win.el
new file mode 100644
index 00000000000..689a5e595a5
--- /dev/null
+++ b/lisp/term/pgtk-win.el
@@ -0,0 +1,504 @@
+;;; xterm.el --- define function key sequences and standard colors for xterm -*- lexical-binding: t -*-
+
+;; Copyright (C) 1995, 2001-2020 Free Software Foundation, Inc.
+
+;; Author: FSF
+;; Keywords: terminals
+
+;; 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/>.
+
+;;; Commentary:
+
+;;; Code:
+(eval-when-compile (require 'cl-lib))
+(or (featurep 'pgtk)
+ (error "%s: Loading pgtk-win.el but not compiled for pure Gtk+-3."
+ invocation-name))
+
+;; Documentation-purposes only: actually loaded in loadup.el.
+(require 'term/common-win)
+(require 'frame)
+(require 'mouse)
+(require 'scroll-bar)
+(require 'faces)
+(require 'menu-bar)
+(require 'fontset)
+(require 'dnd)
+
+(defgroup pgtk nil
+ "Pure-GTK specific features."
+ :group 'environment)
+
+;;;; Command line argument handling.
+
+(defvar x-invocation-args)
+;; Set in term/common-win.el; currently unused by Gtk's x-open-connection.
+(defvar x-command-line-resources)
+
+;; pgtkterm.c.
+(defvar pgtk-input-file)
+
+(declare-function pgtk-use-im-context "pgtkim.c")
+(defvar pgtk-use-im-context-on-new-connection)
+
+(defun pgtk-handle-nxopen (_switch &optional temp)
+ (setq unread-command-events (append unread-command-events
+ (if temp '(pgtk-open-temp-file)
+ '(pgtk-open-file)))
+ pgtk-input-file (append pgtk-input-file (list (pop x-invocation-args)))))
+
+(defun pgtk-handle-nxopentemp (switch)
+ (pgtk-handle-nxopen switch t))
+
+(defun pgtk-ignore-1-arg (_switch)
+ (setq x-invocation-args (cdr x-invocation-args)))
+
+;;;; File handling.
+
+(declare-function pgtk-hide-emacs "pgtkfns.c" (on))
+
+
+(defun pgtk-drag-n-drop (event &optional new-frame force-text)
+ "Edit the files listed in the drag-n-drop EVENT.
+Switch to a buffer editing the last file dropped."
+ (interactive "e")
+ (let* ((window (posn-window (event-start event)))
+ (arg (car (cdr (cdr event))))
+ (type (car arg))
+ (data (car (cdr arg)))
+ (url-or-string (cond ((eq type 'file)
+ (concat "file:" data))
+ (t data))))
+ (set-frame-selected-window nil window)
+ (when new-frame
+ (select-frame (make-frame)))
+ (raise-frame)
+ (setq window (selected-window))
+ (if force-text
+ (dnd-insert-text window 'private data)
+ (dnd-handle-one-url window 'private url-or-string))))
+
+
+(defun pgtk-drag-n-drop-other-frame (event)
+ "Edit the files listed in the drag-n-drop EVENT, in other frames.
+May create new frames, or reuse existing ones. The frame editing
+the last file dropped is selected."
+ (interactive "e")
+ (pgtk-drag-n-drop event t))
+
+(defun pgtk-drag-n-drop-as-text (event)
+ "Drop the data in EVENT as text."
+ (interactive "e")
+ (pgtk-drag-n-drop event nil t))
+
+(defun pgtk-drag-n-drop-as-text-other-frame (event)
+ "Drop the data in EVENT as text in a new frame."
+ (interactive "e")
+ (pgtk-drag-n-drop event t t))
+
+(global-set-key [drag-n-drop] 'pgtk-drag-n-drop)
+(global-set-key [C-drag-n-drop] 'pgtk-drag-n-drop-other-frame)
+(global-set-key [M-drag-n-drop] 'pgtk-drag-n-drop-as-text)
+(global-set-key [C-M-drag-n-drop] 'pgtk-drag-n-drop-as-text-other-frame)
+
+;;;; Frame-related functions.
+
+;; pgtkterm.c
+(defvar pgtk-alternate-modifier)
+(defvar pgtk-right-alternate-modifier)
+(defvar pgtk-right-command-modifier)
+(defvar pgtk-right-control-modifier)
+
+;; You say tomAYto, I say tomAHto..
+(with-no-warnings
+ (defvaralias 'pgtk-option-modifier 'pgtk-alternate-modifier)
+ (defvaralias 'pgtk-right-option-modifier 'pgtk-right-alternate-modifier))
+
+(defun pgtk-do-hide-emacs ()
+ (interactive)
+ (pgtk-hide-emacs t))
+
+(declare-function pgtk-hide-others "pgtkfns.c" ())
+
+(defun pgtk-do-hide-others ()
+ (interactive)
+ (pgtk-hide-others))
+
+(declare-function pgtk-emacs-info-panel "pgtkfns.c" ())
+
+(defun pgtk-do-emacs-info-panel ()
+ (interactive)
+ (pgtk-emacs-info-panel))
+
+(defun pgtk-next-frame ()
+ "Switch to next visible frame."
+ (interactive)
+ (other-frame 1))
+
+(defun pgtk-prev-frame ()
+ "Switch to previous visible frame."
+ (interactive)
+ (other-frame -1))
+
+;; Frame will be focused anyway, so select it
+;; (if this is not done, mode line is dimmed until first interaction)
+;; FIXME: Sounds like we're working around a bug in the underlying code.
+(add-hook 'after-make-frame-functions 'select-frame)
+
+(defvar tool-bar-mode)
+(declare-function tool-bar-mode "tool-bar" (&optional arg))
+
+;; Based on a function by David Reitter <dreitter@inf.ed.ac.uk> ;
+;; see https://lists.gnu.org/archive/html/emacs-devel/2005-09/msg00681.html .
+(defun pgtk-toggle-toolbar (&optional frame)
+ "Switches the tool bar on and off in frame FRAME.
+ If FRAME is nil, the change applies to the selected frame."
+ (interactive)
+ (modify-frame-parameters
+ frame (list (cons 'tool-bar-lines
+ (if (> (or (frame-parameter frame 'tool-bar-lines) 0) 0)
+ 0 1)) ))
+ (if (not tool-bar-mode) (tool-bar-mode t)))
+
+
+;;;; Dialog-related functions.
+
+;; Ask user for confirm before printing. Due to Kevin Rodgers.
+(defun pgtk-print-buffer ()
+ "Interactive front-end to `print-buffer': asks for user confirmation first."
+ (interactive)
+ (if (and (called-interactively-p 'interactive)
+ (or (listp last-nonmenu-event)
+ (and (char-or-string-p (event-basic-type last-command-event))
+ (memq 'super (event-modifiers last-command-event)))))
+ (let ((last-nonmenu-event (if (listp last-nonmenu-event)
+ last-nonmenu-event
+ ;; Fake it:
+ `(mouse-1 POSITION 1))))
+ (if (y-or-n-p (format "Print buffer %s? " (buffer-name)))
+ (print-buffer)
+ (error "Canceled")))
+ (print-buffer)))
+
+;;;; Font support.
+
+;; Needed for font listing functions under both backend and normal
+(setq scalable-fonts-allowed t)
+
+;; Default fontset. This is mainly here to show how a fontset
+;; can be set up manually. Ordinarily, fontsets are auto-created whenever
+;; a font is chosen by
+(defvar pgtk-standard-fontset-spec
+ ;; Only some code supports this so far, so use uglier XLFD version
+ ;; "-pgtk-*-*-*-*-*-10-*-*-*-*-*-fontset-standard,latin:Courier,han:Kai"
+ (mapconcat 'identity
+ '("-*-Monospace-*-*-*-*-10-*-*-*-*-*-fontset-standard"
+ "latin:-*-Courier-*-*-*-*-10-*-*-*-*-*-iso10646-1")
+ ",")
+ "String of fontset spec of the standard fontset.
+This defines a fontset consisting of the Courier and other fonts.
+See the documentation of `create-fontset-from-fontset-spec' for the format.")
+
+
+;;;; Pasteboard support.
+
+(define-obsolete-function-alias 'pgtk-store-cut-buffer-internal
+ 'gui-set-selection "24.1")
+
+
+(defun pgtk-copy-including-secondary ()
+ (interactive)
+ (call-interactively 'kill-ring-save)
+ (gui-set-selection 'SECONDARY (buffer-substring (point) (mark t))))
+
+(defun pgtk-paste-secondary ()
+ (interactive)
+ (insert (gui-get-selection 'SECONDARY)))
+
+
+(defun pgtk-suspend-error ()
+ ;; Don't allow suspending if any of the frames are PGTK frames.
+ (if (memq 'pgtk (mapcar 'window-system (frame-list)))
+ (error "Cannot suspend Emacs while a PGTK GUI frame exists")))
+
+
+
+(defvar pgtk-initialized nil
+ "Non-nil if pure-GTK windowing has been initialized.")
+
+(declare-function x-handle-args "common-win" (args))
+(declare-function x-open-connection "pgtkfns.c"
+ (display &optional xrm-string must-succeed))
+(declare-function pgtk-set-resource "pgtkfns.c" (owner name value))
+
+;; Do the actual pure-GTK Windows setup here; the above code just
+;; defines functions and variables that we use now.
+(cl-defmethod window-system-initialization (&context (window-system pgtk)
+ &optional display)
+ "Initialize Emacs for pure-GTK windowing."
+ (cl-assert (not pgtk-initialized))
+
+ ;; PENDING: not needed?
+ (setq command-line-args (x-handle-args command-line-args))
+
+ ;; Make sure we have a valid resource name.
+ (or (stringp x-resource-name)
+ (let (i)
+ (setq x-resource-name (copy-sequence invocation-name))
+
+ ;; Change any . or * characters in x-resource-name to hyphens,
+ ;; so as not to choke when we use it in X resource queries.
+ (while (setq i (string-match "[.*]" x-resource-name))
+ (aset x-resource-name i ?-))))
+
+ ;; Setup the default fontset.
+ (create-default-fontset)
+ ;; Create the standard fontset.
+ (condition-case err
+ (create-fontset-from-fontset-spec pgtk-standard-fontset-spec t)
+ (error (display-warning
+ 'initialization
+ (format "Creation of the standard fontset failed: %s" err)
+ :error)))
+
+ (x-open-connection (or display
+ x-display-name)
+ x-command-line-resources
+ ;; Exit Emacs with fatal error if this fails and we
+ ;; are the initial display.
+ (= (length (frame-list)) 0))
+
+ (x-apply-session-resources)
+
+ ;; Don't let Emacs suspend under PGTK.
+ (add-hook 'suspend-hook 'pgtk-suspend-error)
+
+ (setq pgtk-initialized t))
+
+;; Any display name is OK.
+(add-to-list 'display-format-alist '(".*" . pgtk))
+(cl-defmethod handle-args-function (args &context (window-system pgtk))
+ (x-handle-args args))
+
+(cl-defmethod frame-creation-function (params &context (window-system pgtk))
+ (x-create-frame-with-faces params))
+
+(declare-function pgtk-own-selection-internal "pgtkselect.c" (selection value &optional frame))
+(declare-function pgtk-disown-selection-internal "pgtkselect.c" (selection &optional time_object terminal))
+(declare-function pgtk-selection-owner-p "pgtkselect.c" (&optional selection terminal))
+(declare-function pgtk-selection-exists-p "pgtkselect.c" (&optional selection terminal))
+(declare-function pgtk-get-selection-internal "pgtkselect.c" (selection-symbol target-type &optional time_stamp terminal))
+
+(cl-defmethod gui-backend-set-selection (selection value
+ &context (window-system pgtk))
+ (if value (pgtk-own-selection-internal selection value)
+ (pgtk-disown-selection-internal selection)))
+
+(cl-defmethod gui-backend-selection-owner-p (selection
+ &context (window-system pgtk))
+ (pgtk-selection-owner-p selection))
+
+(cl-defmethod gui-backend-selection-exists-p (selection
+ &context (window-system pgtk))
+ (pgtk-selection-exists-p selection))
+
+(cl-defmethod gui-backend-get-selection (selection-symbol target-type
+ &context (window-system pgtk))
+ (pgtk-get-selection-internal selection-symbol target-type))
+
+
+(defvar pgtk-preedit-overlay nil)
+
+(defun pgtk-preedit-text (event)
+ "An internal function to display preedit text from input method.
+
+EVENT is an event of PGTK_PREEDIT_TEXT_EVENT.
+It contains colors and texts."
+ (interactive "e")
+ (when pgtk-preedit-overlay
+ (delete-overlay pgtk-preedit-overlay))
+ (setq pgtk-preedit-overlay nil)
+
+ (let ((ovstr "")
+ (idx 0)
+ atts ov str color face-name)
+ (dolist (part (nth 1 event))
+ (setq str (car part))
+ (setq face-name (intern (format "pgtk-im-%d" idx)))
+ (eval
+ `(defface ,face-name nil "face of input method preedit"))
+ (setq atts nil)
+ (when (setq color (cdr-safe (assq 'fg (cdr part))))
+ (setq atts (append atts `(:foreground ,color))))
+ (when (setq color (cdr-safe (assq 'bg (cdr part))))
+ (setq atts (append atts `(:background ,color))))
+ (when (setq color (cdr-safe (assq 'ul (cdr part))))
+ (setq atts (append atts `(:underline ,color))))
+ (face-spec-set face-name `((t . ,atts)))
+ (add-text-properties 0 (length str) `(face ,face-name) str)
+ (setq ovstr (concat ovstr str))
+ (setq idx (1+ idx)))
+
+ (setq ov (make-overlay (point) (point)))
+ (overlay-put ov 'before-string ovstr)
+ (setq pgtk-preedit-overlay ov)))
+
+
+(add-hook 'after-init-hook
+ (function
+ (lambda ()
+ (when (eq window-system 'pgtk)
+ (pgtk-use-im-context pgtk-use-im-context-on-new-connection)))))
+
+
+;;;
+
+(defcustom x-gtk-stock-map
+ (mapcar (lambda (arg)
+ (cons (purecopy (car arg)) (purecopy (cdr arg))))
+ '(
+ ("etc/images/new" . ("document-new" "gtk-new"))
+ ("etc/images/open" . ("document-open" "gtk-open"))
+ ("etc/images/diropen" . "n:system-file-manager")
+ ("etc/images/close" . ("window-close" "gtk-close"))
+ ("etc/images/save" . ("document-save" "gtk-save"))
+ ("etc/images/saveas" . ("document-save-as" "gtk-save-as"))
+ ("etc/images/undo" . ("edit-undo" "gtk-undo"))
+ ("etc/images/cut" . ("edit-cut" "gtk-cut"))
+ ("etc/images/copy" . ("edit-copy" "gtk-copy"))
+ ("etc/images/paste" . ("edit-paste" "gtk-paste"))
+ ("etc/images/search" . ("edit-find" "gtk-find"))
+ ("etc/images/print" . ("document-print" "gtk-print"))
+ ("etc/images/preferences" . ("preferences-system" "gtk-preferences"))
+ ("etc/images/help" . ("help-browser" "gtk-help"))
+ ("etc/images/left-arrow" . ("go-previous" "gtk-go-back"))
+ ("etc/images/right-arrow" . ("go-next" "gtk-go-forward"))
+ ("etc/images/home" . ("go-home" "gtk-home"))
+ ("etc/images/jump-to" . ("go-jump" "gtk-jump-to"))
+ ("etc/images/index" . ("gtk-search" "gtk-index"))
+ ("etc/images/exit" . ("application-exit" "gtk-quit"))
+ ("etc/images/cancel" . "gtk-cancel")
+ ("etc/images/info" . ("dialog-information" "gtk-info"))
+ ("etc/images/bookmark_add" . "n:bookmark_add")
+ ;; Used in Gnus and/or MH-E:
+ ("etc/images/attach" . ("mail-attachment" "gtk-attach"))
+ ("etc/images/connect" . "gtk-connect")
+ ("etc/images/contact" . "gtk-contact")
+ ("etc/images/delete" . ("edit-delete" "gtk-delete"))
+ ("etc/images/describe" . ("document-properties" "gtk-properties"))
+ ("etc/images/disconnect" . "gtk-disconnect")
+ ;; ("etc/images/exit" . "gtk-exit")
+ ("etc/images/lock-broken" . "gtk-lock_broken")
+ ("etc/images/lock-ok" . "gtk-lock_ok")
+ ("etc/images/lock" . "gtk-lock")
+ ("etc/images/next-page" . "gtk-next-page")
+ ("etc/images/refresh" . ("view-refresh" "gtk-refresh"))
+ ("etc/images/search-replace" . "edit-find-replace")
+ ("etc/images/sort-ascending" . ("view-sort-ascending" "gtk-sort-ascending"))
+ ("etc/images/sort-column-ascending" . "gtk-sort-column-ascending")
+ ("etc/images/sort-criteria" . "gtk-sort-criteria")
+ ("etc/images/sort-descending" . ("view-sort-descending"
+ "gtk-sort-descending"))
+ ("etc/images/sort-row-ascending" . "gtk-sort-row-ascending")
+ ("etc/images/spell" . ("tools-check-spelling" "gtk-spell-check"))
+ ("images/gnus/toggle-subscription" . "gtk-task-recurring")
+ ("images/mail/compose" . ("mail-message-new" "gtk-mail-compose"))
+ ("images/mail/copy" . "gtk-mail-copy")
+ ("images/mail/forward" . "gtk-mail-forward")
+ ("images/mail/inbox" . "gtk-inbox")
+ ("images/mail/move" . "gtk-mail-move")
+ ("images/mail/not-spam" . "gtk-not-spam")
+ ("images/mail/outbox" . "gtk-outbox")
+ ("images/mail/reply-all" . "gtk-mail-reply-to-all")
+ ("images/mail/reply" . "gtk-mail-reply")
+ ("images/mail/save-draft" . "gtk-mail-handling")
+ ("images/mail/send" . ("mail-send" "gtk-mail-send"))
+ ("images/mail/spam" . "gtk-spam")
+ ;; Used for GDB Graphical Interface
+ ("images/gud/break" . "gtk-no")
+ ("images/gud/recstart" . ("media-record" "gtk-media-record"))
+ ("images/gud/recstop" . ("media-playback-stop" "gtk-media-stop"))
+ ;; No themed versions available:
+ ;; mail/preview (combining stock_mail and stock_zoom)
+ ;; mail/save (combining stock_mail, stock_save and stock_convert)
+ ))
+ "How icons for tool bars are mapped to Gtk+ stock items.
+Emacs must be compiled with the Gtk+ toolkit for this to have any effect.
+A value that begins with n: denotes a named icon instead of a stock icon."
+ :version "22.2"
+ :type '(choice (repeat
+ (choice symbol
+ (cons (string :tag "Emacs icon")
+ (choice (group (string :tag "Named")
+ (string :tag "Stock"))
+ (string :tag "Stock/named"))))))
+ :group 'pgtk)
+
+(defcustom icon-map-list '(x-gtk-stock-map)
+ "A list of alists that map icon file names to stock/named icons.
+The alists are searched in the order they appear. The first match is used.
+The keys in the alists are file names without extension and with two directory
+components. For example, to map /usr/share/emacs/22.1.1/etc/images/open.xpm
+to stock item gtk-open, use:
+
+ (\"etc/images/open\" . \"gtk-open\")
+
+Themes also have named icons. To map to one of those, use n: before the name:
+
+ (\"etc/images/diropen\" . \"n:system-file-manager\")
+
+The list elements are either the symbol name for the alist or the
+alist itself.
+
+If you don't want stock icons, set the variable to nil."
+ :version "22.2"
+ :type '(choice (const :tag "Don't use stock icons" nil)
+ (repeat (choice symbol
+ (cons (string :tag "Emacs icon")
+ (string :tag "Stock/named")))))
+ :group 'pgtk)
+
+(defconst x-gtk-stock-cache (make-hash-table :weakness t :test 'equal))
+
+(defun x-gtk-map-stock (file)
+ "Map icon with file name FILE to a Gtk+ stock name.
+This uses `icon-map-list' to map icon file names to stock icon names."
+ (when (stringp file)
+ (or (gethash file x-gtk-stock-cache)
+ (puthash
+ file
+ (save-match-data
+ (let* ((file-sans (file-name-sans-extension file))
+ (key (and (string-match "/\\([^/]+/[^/]+/[^/]+$\\)"
+ file-sans)
+ (match-string 1 file-sans)))
+ (icon-map icon-map-list)
+ elem value)
+ (while (and (null value) icon-map)
+ (setq elem (car icon-map)
+ value (assoc-string (or key file-sans)
+ (if (symbolp elem)
+ (symbol-value elem)
+ elem))
+ icon-map (cdr icon-map)))
+ (and value (cdr value))))
+ x-gtk-stock-cache))))
+
+
+(provide 'pgtk-win)
+(provide 'term/pgtk-win)
+
+;;; pgtk-win.el ends here
diff --git a/lisp/url/url-privacy.el b/lisp/url/url-privacy.el
index 58ae6efbfc1..ebba87ebbb5 100644
--- a/lisp/url/url-privacy.el
+++ b/lisp/url/url-privacy.el
@@ -48,6 +48,7 @@
(pcase (or window-system 'tty)
('x "X11")
('ns "OpenStep")
+ ('pgtk "PureGTK")
('tty "TTY")
(_ nil)))))
diff --git a/m4/gsettings.m4 b/m4/gsettings.m4
new file mode 100644
index 00000000000..882e6a83e76
--- /dev/null
+++ b/m4/gsettings.m4
@@ -0,0 +1,88 @@
+# Increment this whenever this file is changed.
+#serial 2
+
+dnl GLIB_GSETTINGS
+dnl Defines GSETTINGS_SCHEMAS_INSTALL which controls whether
+dnl the schema should be compiled
+dnl
+
+AC_DEFUN([GLIB_GSETTINGS],
+[
+ dnl We can't use PKG_PREREQ because that needs 0.29.
+ m4_ifndef([PKG_PROG_PKG_CONFIG],
+ [pkg.m4 version 0.28 or later is required])
+
+ m4_pattern_allow([AM_V_GEN])
+ AC_ARG_ENABLE(schemas-compile,
+ AS_HELP_STRING([--disable-schemas-compile],
+ [Disable regeneration of gschemas.compiled on install]),
+ [case ${enableval} in
+ yes) GSETTINGS_DISABLE_SCHEMAS_COMPILE="" ;;
+ no) GSETTINGS_DISABLE_SCHEMAS_COMPILE="1" ;;
+ *) AC_MSG_ERROR([bad value ${enableval} for --enable-schemas-compile]) ;;
+ esac])
+ AC_SUBST([GSETTINGS_DISABLE_SCHEMAS_COMPILE])
+ PKG_PROG_PKG_CONFIG([0.16])
+ AC_SUBST(gsettingsschemadir, [${datadir}/glib-2.0/schemas])
+ AS_IF([test x$cross_compiling != xyes],
+ [PKG_CHECK_VAR([GLIB_COMPILE_SCHEMAS], [gio-2.0], [glib_compile_schemas])],
+ [AC_PATH_PROG([GLIB_COMPILE_SCHEMAS], [glib-compile-schemas])])
+ AC_SUBST(GLIB_COMPILE_SCHEMAS)
+ if test "x$GLIB_COMPILE_SCHEMAS" = "x"; then
+ ifelse([$2],,[AC_MSG_ERROR([glib-compile-schemas not found.])],[$2])
+ else
+ ifelse([$1],,[:],[$1])
+ fi
+
+ GSETTINGS_RULES='
+.PHONY : uninstall-gsettings-schemas install-gsettings-schemas clean-gsettings-schemas
+
+mostlyclean-am: clean-gsettings-schemas
+
+gsettings__enum_file = $(addsuffix .enums.xml,$(gsettings_ENUM_NAMESPACE))
+
+%.gschema.valid: %.gschema.xml $(gsettings__enum_file)
+ $(AM_V_GEN) $(GLIB_COMPILE_SCHEMAS) --strict --dry-run $(addprefix --schema-file=,$(gsettings__enum_file)) --schema-file=$< && mkdir -p [$](@D) && touch [$]@
+
+all-am: $(gsettings_SCHEMAS:.xml=.valid)
+uninstall-am: uninstall-gsettings-schemas
+install-data-am: install-gsettings-schemas
+
+.SECONDARY: $(gsettings_SCHEMAS)
+
+install-gsettings-schemas: $(gsettings_SCHEMAS) $(gsettings__enum_file)
+ @$(NORMAL_INSTALL)
+ if test -n "$^"; then \
+ test -z "$(gsettingsschemadir)" || $(MKDIR_P) "$(DESTDIR)$(gsettingsschemadir)"; \
+ $(INSTALL_DATA) $^ "$(DESTDIR)$(gsettingsschemadir)"; \
+ test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir); \
+ fi
+
+uninstall-gsettings-schemas:
+ @$(NORMAL_UNINSTALL)
+ @list='\''$(gsettings_SCHEMAS) $(gsettings__enum_file)'\''; test -n "$(gsettingsschemadir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e '\''s|^.*/||'\''`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '\''$(DESTDIR)$(gsettingsschemadir)'\'' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(gsettingsschemadir)" && rm -f $$files
+ test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir)
+
+clean-gsettings-schemas:
+ rm -f $(gsettings_SCHEMAS:.xml=.valid) $(gsettings__enum_file)
+
+ifdef gsettings_ENUM_NAMESPACE
+$(gsettings__enum_file): $(gsettings_ENUM_FILES)
+ $(AM_V_GEN) glib-mkenums --comments '\''<!-- @comment@ -->'\'' --fhead "<schemalist>" --vhead " <@type@ id='\''$(gsettings_ENUM_NAMESPACE).@EnumName@'\''>" --vprod " <value nick='\''@valuenick@'\'' value='\''@valuenum@'\''/>" --vtail " </@type@>" --ftail "</schemalist>" [$]^ > [$]@.tmp && mv [$]@.tmp [$]@
+endif
+'
+ _GSETTINGS_SUBST(GSETTINGS_RULES)
+])
+
+dnl _GSETTINGS_SUBST(VARIABLE)
+dnl Abstract macro to do either _AM_SUBST_NOTMAKE or AC_SUBST
+AC_DEFUN([_GSETTINGS_SUBST],
+[
+AC_SUBST([$1])
+m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([$1])])
+]
+)
diff --git a/src/.gdbinit b/src/.gdbinit
index f74e295f7ea..68db1ff3ea4 100644
--- a/src/.gdbinit
+++ b/src/.gdbinit
@@ -41,6 +41,11 @@ handle SIGUSR2 noprint pass
# debugging.
handle SIGALRM ignore
+# On selection send failed.
+if defined_HAVE_PGTK
+ handle SIGPIPE nostop noprint
+end
+
# Use $bugfix so that the value isn't a constant.
# Using a constant runs into GDB bugs sometimes.
define xgetptr
@@ -1224,6 +1229,9 @@ set print pretty on
set print sevenbit-strings
show environment DISPLAY
+if defined_HAVE_PGTK
+ show environment WAYLAND_DISPLAY
+end
show environment TERM
# When debugging, it is handy to be able to "return" from
diff --git a/src/Makefile.in b/src/Makefile.in
index 3a8445db2d4..ee9a22469ea 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -297,6 +297,9 @@ W32_OBJ=@W32_OBJ@
## -lkernel32 if CYGWIN but not HAVE_W32, else empty.
W32_LIBS=@W32_LIBS@
+PGTK_OBJ=@PGTK_OBJ@
+PGTK_LIBS=@PGTK_LIBS@
+
## emacs.res if HAVE_W32
EMACSRES = @EMACSRES@
## If HAVE_W32, compiler arguments for including
@@ -432,7 +435,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \
$(if $(HYBRID_MALLOC),sheap.o) \
$(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \
$(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) $(JSON_OBJ) \
- $(HAIKU_OBJ)
+ $(HAIKU_OBJ) $(PGTK_OBJ)
doc_obj = $(base_obj) $(NS_OBJC_OBJ)
obj = $(doc_obj) $(HAIKU_CXX_OBJ)
@@ -539,7 +542,7 @@ export LISP_PRELOADED = ${shortlisp}
lisp = $(addprefix ${lispsource}/,${shortlisp})
## Construct full set of libraries to be linked.
-LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \
+LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(PGTK_LIBS) $(LIBX_BASE) $(LIBIMAGE) \
$(LIBX_OTHER) $(LIBSOUND) \
$(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_ACL) $(LIB_CLOCK_GETTIME) \
$(WEBKIT_LIBS) \
diff --git a/src/alloc.c b/src/alloc.c
index 9f52a414d68..16f9076b03c 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -6145,6 +6145,9 @@ garbage_collect (void)
mark_terminals ();
mark_kboards ();
mark_threads ();
+#ifdef HAVE_PGTK
+ mark_pgtkterm ();
+#endif
#ifdef USE_GTK
xg_mark_data ();
@@ -7721,6 +7724,12 @@ enum defined_HAVE_X_WINDOWS { defined_HAVE_X_WINDOWS = true };
enum defined_HAVE_X_WINDOWS { defined_HAVE_X_WINDOWS = false };
#endif
+#ifdef HAVE_PGTK
+enum defined_HAVE_PGTK { defined_HAVE_PGTK = true };
+#else
+enum defined_HAVE_PGTK { defined_HAVE_PGTK = false };
+#endif
+
/* When compiled with GCC, GDB might say "No enum type named
pvec_type" if we don't have at least one symbol with that type, and
then xbacktrace could fail. Similarly for the other enums and
@@ -7740,5 +7749,6 @@ union
enum More_Lisp_Bits More_Lisp_Bits;
enum pvec_type pvec_type;
enum defined_HAVE_X_WINDOWS defined_HAVE_X_WINDOWS;
+ enum defined_HAVE_PGTK defined_HAVE_PGTK;
} const EXTERNALLY_VISIBLE gdb_make_enums_visible = {0};
#endif /* __GNUC__ */
diff --git a/src/dispextern.h b/src/dispextern.h
index ff4e7293d85..0f316a2eaf9 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -134,6 +134,14 @@ typedef Emacs_Pixmap Emacs_Pix_Context;
#define FACE_COLOR_TO_PIXEL(face_color, frame) face_color
#endif
+#ifdef HAVE_PGTK
+#include "pgtkgui.h"
+/* Following typedef needed to accommodate the MSDOS port, believe it or not. */
+typedef struct pgtk_display_info Display_Info;
+typedef Emacs_Pixmap XImagePtr;
+typedef XImagePtr XImagePtr_or_DC;
+#endif /* HAVE_PGTK */
+
#ifdef HAVE_HAIKU
#include "haikugui.h"
typedef struct haiku_display_info Display_Info;
@@ -1400,6 +1408,9 @@ struct glyph_string
Emacs_GC *gc;
HDC hdc;
#endif
+#if defined (HAVE_PGTK)
+ Emacs_GC xgcv;
+#endif
/* A pointer to the first glyph in the string. This glyph
corresponds to char2b[0]. Needed to draw rectangles if
diff --git a/src/dispnew.c b/src/dispnew.c
index a976bf94c5e..4faa7a7777b 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -6453,6 +6453,15 @@ init_display_interactive (void)
}
#endif
+#ifdef HAVE_PGTK
+ if (!inhibit_window_system && !will_dump_p ())
+ {
+ Vinitial_window_system = Qpgtk;
+ Vwindow_system_version = make_fixnum (3);
+ return;
+ }
+#endif
+
#ifdef HAVE_HAIKU
if (!inhibit_window_system && !will_dump_p ())
{
diff --git a/src/emacs.c b/src/emacs.c
index 3fc055aed92..6048d126781 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1909,6 +1909,9 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
init_bignum ();
init_threads ();
init_eval ();
+#ifdef HAVE_PGTK
+ init_pgtkterm (); /* before init_atimer(). */
+#endif
running_asynch_code = 0;
init_random ();
@@ -2245,6 +2248,15 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
syms_of_fontset ();
#endif /* HAVE_NS */
+#ifdef HAVE_PGTK
+ syms_of_pgtkterm ();
+ syms_of_pgtkfns ();
+ syms_of_pgtkselect ();
+ syms_of_pgtkmenu ();
+ syms_of_pgtkim ();
+ syms_of_fontset ();
+ syms_of_xsettings ();
+#endif /* HAVE_PGTK */
#ifdef HAVE_HAIKU
syms_of_haikuterm ();
syms_of_haikufns ();
@@ -2328,7 +2340,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#ifdef HAVE_DBUS
init_dbusbind ();
#endif
-#ifdef USE_GTK
+#if defined(USE_GTK) && !defined(HAVE_PGTK)
init_xterm ();
#endif
diff --git a/src/emacsgtkfixed.c b/src/emacsgtkfixed.c
index 1f5b0947e5c..78c952f8054 100644
--- a/src/emacsgtkfixed.c
+++ b/src/emacsgtkfixed.c
@@ -22,7 +22,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "lisp.h"
#include "frame.h"
+#ifdef HAVE_PGTK
+#include "pgtkterm.h"
+#else
#include "xterm.h"
+#endif
#include "xwidget.h"
#include "emacsgtkfixed.h"
@@ -47,7 +51,9 @@ static void emacs_fixed_get_preferred_width (GtkWidget *widget,
static void emacs_fixed_get_preferred_height (GtkWidget *widget,
gint *minimum,
gint *natural);
+#ifndef HAVE_PGTK
static GType emacs_fixed_get_type (void);
+#endif
G_DEFINE_TYPE (EmacsFixed, emacs_fixed, GTK_TYPE_FIXED)
static EmacsFixed *
@@ -93,9 +99,15 @@ emacs_fixed_get_preferred_width (GtkWidget *widget,
{
EmacsFixed *fixed = EMACS_FIXED (widget);
EmacsFixedPrivate *priv = fixed->priv;
+#ifdef HAVE_PGTK
+ int w = priv->f->output_data.pgtk->size_hints.min_width;
+ if (minimum) *minimum = w;
+ if (natural) *natural = priv->f->output_data.pgtk->preferred_width;
+#else
int w = priv->f->output_data.x->size_hints.min_width;
if (minimum) *minimum = w;
if (natural) *natural = w;
+#endif
}
static void
@@ -105,12 +117,20 @@ emacs_fixed_get_preferred_height (GtkWidget *widget,
{
EmacsFixed *fixed = EMACS_FIXED (widget);
EmacsFixedPrivate *priv = fixed->priv;
+#ifdef HAVE_PGTK
+ int h = priv->f->output_data.pgtk->size_hints.min_height;
+ if (minimum) *minimum = h;
+ if (natural) *natural = priv->f->output_data.pgtk->preferred_height;
+#else
int h = priv->f->output_data.x->size_hints.min_height;
if (minimum) *minimum = h;
if (natural) *natural = h;
+#endif
}
+#ifndef HAVE_PGTK
+
/* Override the X function so we can intercept Gtk+ 3 calls.
Use our values for min_width/height so that KDE don't freak out
(Bug#8919), and so users can resize our frames as they wish. */
@@ -145,8 +165,13 @@ XSetWMSizeHints (Display *d,
if ((hints->flags & PMinSize) && f)
{
+#ifdef HAVE_PGTK
+ int w = f->output_data.pgtk->size_hints.min_width;
+ int h = f->output_data.pgtk->size_hints.min_height;
+#else
int w = f->output_data.x->size_hints.min_width;
int h = f->output_data.x->size_hints.min_height;
+#endif
data[5] = w;
data[6] = h;
}
@@ -164,3 +189,5 @@ XSetWMNormalHints (Display *d, Window w, XSizeHints *hints)
{
XSetWMSizeHints (d, w, hints, XA_WM_NORMAL_HINTS);
}
+
+#endif
diff --git a/src/emacsgtkfixed.h b/src/emacsgtkfixed.h
index 78879764d86..4f7a4eb3f71 100644
--- a/src/emacsgtkfixed.h
+++ b/src/emacsgtkfixed.h
@@ -27,6 +27,11 @@ struct frame;
G_BEGIN_DECLS
+#ifdef HAVE_PGTK
+#define EMACS_TYPE_FIXED (emacs_fixed_get_type ())
+#define EMACS_IS_FIXED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EMACS_TYPE_FIXED))
+#endif
+
struct frame;
typedef struct _EmacsFixedPrivate EmacsFixedPrivate;
@@ -44,6 +49,10 @@ struct _EmacsFixedClass
GtkFixedClass parent_class;
};
+#ifdef HAVE_PGTK
+extern GType emacs_fixed_get_type (void);
+#endif
+
extern GtkWidget *emacs_fixed_new (struct frame *f);
G_END_DECLS
diff --git a/src/font.c b/src/font.c
index f934a8c0adc..f2fd64e76ee 100644
--- a/src/font.c
+++ b/src/font.c
@@ -5744,7 +5744,11 @@ match. */);
syms_of_xftfont ();
#endif /* HAVE_XFT */
#endif /* not USE_CAIRO */
-#endif /* HAVE_X_WINDOWS */
+#else /* not HAVE_X_WINDOWS */
+#ifdef USE_CAIRO
+ syms_of_ftcrfont ();
+#endif
+#endif /* not HAVE_X_WINDOWS */
#else /* not HAVE_FREETYPE */
#ifdef HAVE_X_WINDOWS
syms_of_xfont ();
diff --git a/src/frame.c b/src/frame.c
index 33e9606e41d..2b06bc821d0 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -225,7 +225,8 @@ Value is:
`x' for an Emacs frame that is really an X window,
`w32' for an Emacs frame that is a window on MS-Windows display,
`ns' for an Emacs frame on a GNUstep or Macintosh Cocoa display,
- `pc' for a direct-write MS-DOS frame.
+ `pc' for a direct-write MS-DOS frame,
+ `pgtk' for an Emacs frame running on pure GTK.
`haiku' for an Emacs frame running in Haiku.
See also `frame-live-p'. */)
(Lisp_Object object)
@@ -245,6 +246,8 @@ See also `frame-live-p'. */)
return Qpc;
case output_ns:
return Qns;
+ case output_pgtk:
+ return Qpgtk;
case output_haiku:
return Qhaiku;
default:
@@ -2215,7 +2218,8 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
/* Since a similar behavior was observed on the Lucid and Motif
builds (see Bug#5802, Bug#21509, Bug#23499, Bug#27816), we now
don't delete the terminal for these builds either. */
- if (terminal->reference_count == 0 && terminal->type == output_x_window)
+ if (terminal->reference_count == 0 &&
+ (terminal->type == output_x_window || terminal->type == output_pgtk))
terminal->reference_count = 1;
#endif /* USE_X_TOOLKIT || USE_GTK */
if (terminal->reference_count == 0)
@@ -5896,7 +5900,7 @@ This function is for internal use only. */)
#ifdef HAVE_WINDOW_SYSTEM
-# if (defined USE_GTK || defined HAVE_NS || defined HAVE_XINERAMA \
+# if (defined USE_GTK || defined HAVE_PGTK || defined HAVE_NS || defined HAVE_XINERAMA \
|| defined HAVE_XRANDR)
void
free_monitors (struct MonitorInfo *monitors, int n_monitors)
@@ -5934,6 +5938,10 @@ make_monitor_attribute_list (struct MonitorInfo *monitors,
attributes);
attributes = Fcons (Fcons (Qframes, AREF (monitor_frames, i)),
attributes);
+#ifdef HAVE_PGTK
+ attributes = Fcons (Fcons (Qscale_factor, make_float (mi->scale_factor)),
+ attributes);
+#endif
attributes = Fcons (Fcons (Qmm_size,
list2i (mi->mm_width, mi->mm_height)),
attributes);
@@ -6023,6 +6031,7 @@ syms_of_frame (void)
DEFSYM (Qw32, "w32");
DEFSYM (Qpc, "pc");
DEFSYM (Qns, "ns");
+ DEFSYM (Qpgtk, "pgtk");
DEFSYM (Qhaiku, "haiku");
DEFSYM (Qvisible, "visible");
DEFSYM (Qbuffer_predicate, "buffer-predicate");
@@ -6046,6 +6055,9 @@ syms_of_frame (void)
DEFSYM (Qworkarea, "workarea");
DEFSYM (Qmm_size, "mm-size");
+#ifdef HAVE_PGTK
+ DEFSYM (Qscale_factor, "scale-factor");
+#endif
DEFSYM (Qframes, "frames");
DEFSYM (Qsource, "source");
diff --git a/src/frame.h b/src/frame.h
index cb2bad71c5d..4060ee65c42 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -585,6 +585,7 @@ struct frame
struct x_output *x; /* From xterm.h. */
struct w32_output *w32; /* From w32term.h. */
struct ns_output *ns; /* From nsterm.h. */
+ struct pgtk_output *pgtk; /* From pgtkterm.h. */
struct haiku_output *haiku; /* From haikuterm.h. */
}
output_data;
@@ -853,6 +854,11 @@ default_pixels_per_inch_y (void)
#else
#define FRAME_NS_P(f) ((f)->output_method == output_ns)
#endif
+#ifndef HAVE_PGTK
+#define FRAME_PGTK_P(f) false
+#else
+#define FRAME_PGTK_P(f) ((f)->output_method == output_pgtk)
+#endif
#ifndef HAVE_HAIKU
#define FRAME_HAIKU_P(f) false
#else
@@ -870,6 +876,9 @@ default_pixels_per_inch_y (void)
#ifdef HAVE_NS
#define FRAME_WINDOW_P(f) FRAME_NS_P(f)
#endif
+#ifdef HAVE_PGTK
+#define FRAME_WINDOW_P(f) FRAME_PGTK_P(f)
+#endif
#ifdef HAVE_HAIKU
#define FRAME_WINDOW_P(f) FRAME_HAIKU_P (f)
#endif
@@ -925,6 +934,8 @@ default_pixels_per_inch_y (void)
/* Scale factor of frame F. */
#if defined HAVE_NS
# define FRAME_SCALE_FACTOR(f) (FRAME_NS_P (f) ? ns_frame_scale_factor (f) : 1)
+#elif defined HAVE_PGTK
+# define FRAME_SCALE_FACTOR(f) (FRAME_PGTK_P (f) ? pgtk_frame_scale_factor (f) : 1)
#else
# define FRAME_SCALE_FACTOR(f) 1
#endif
@@ -1682,7 +1693,7 @@ extern const char *x_get_resource_string (const char *, const char *);
extern void x_sync (struct frame *);
#endif /* HAVE_X_WINDOWS */
-#ifndef HAVE_NS
+#if !defined (HAVE_NS) && !defined (HAVE_PGTK)
/* Set F's bitmap icon, if specified among F's parameters. */
@@ -1718,6 +1729,9 @@ struct MonitorInfo {
Emacs_Rectangle geom, work;
int mm_width, mm_height;
char *name;
+#ifdef HAVE_PGTK
+ double scale_factor;
+#endif
};
extern void free_monitors (struct MonitorInfo *monitors, int n_monitors);
diff --git a/src/fringe.c b/src/fringe.c
index b651a4eb0d9..441146d135d 100644
--- a/src/fringe.c
+++ b/src/fringe.c
@@ -30,6 +30,8 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "termhooks.h"
#include "pdumper.h"
+#include "pgtkterm.h"
+
/* Fringe bitmaps are represented in three different ways:
Logical bitmaps are used internally to denote things like
@@ -1408,7 +1410,7 @@ If BITMAP overrides a standard fringe bitmap, the original bitmap is restored.
On W32 and MAC (little endian), there's no need to do this.
*/
-#if defined (HAVE_X_WINDOWS)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_PGTK)
static const unsigned char swap_nibble[16] = {
0x0, 0x8, 0x4, 0xc, /* 0000 1000 0100 1100 */
0x2, 0xa, 0x6, 0xe, /* 0010 1010 0110 1110 */
@@ -1471,6 +1473,25 @@ init_fringe_bitmap (int which, struct fringe_bitmap *fb, int once_p)
#endif /* not USE_CAIRO */
#endif /* HAVE_X_WINDOWS */
+#if !defined(HAVE_X_WINDOWS) && defined (HAVE_PGTK)
+ unsigned short *bits = fb->bits;
+ int j;
+
+ for (j = 0; j < fb->height; j++)
+ {
+ unsigned short b = *bits;
+#ifdef WORDS_BIGENDIAN
+ *bits++ = (b << (16 - fb->width));
+#else
+ b = (unsigned short)((swap_nibble[b & 0xf] << 12)
+ | (swap_nibble[(b>>4) & 0xf] << 8)
+ | (swap_nibble[(b>>8) & 0xf] << 4)
+ | (swap_nibble[(b>>12) & 0xf]));
+ *bits++ = (b >> (16 - fb->width));
+#endif
+ }
+#endif /* !HAVE_X_WINDOWS && HAVE_PGTK */
+
#ifdef HAVE_NTGUI
unsigned short *bits = fb->bits;
int j;
diff --git a/src/ftcrfont.c b/src/ftcrfont.c
index 820b3c0bd0c..49b179b0efc 100644
--- a/src/ftcrfont.c
+++ b/src/ftcrfont.c
@@ -24,10 +24,12 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "lisp.h"
#ifdef HAVE_X_WINDOWS
#include "xterm.h"
-#else /* Otherwise, Haiku */
+#elif HAVE_HAIKU
#include "haikuterm.h"
#include "haiku_support.h"
#include "termchar.h"
+#else
+#include "pgtkterm.h"
#endif
#include "blockinput.h"
#include "charset.h"
@@ -526,8 +528,12 @@ ftcrfont_draw (struct glyph_string *s,
block_input ();
#ifndef USE_BE_CAIRO
+#ifdef HAVE_X_WINDOWS
cr = x_begin_cr_clip (f, s->gc);
#else
+ cr = pgtk_begin_cr_clip (f);
+#endif
+#else
BView_draw_lock (FRAME_HAIKU_VIEW (f));
EmacsWindow_begin_cr_critical_section (FRAME_HAIKU_WINDOW (f));
cr = haiku_begin_cr_clip (f, s);
@@ -551,8 +557,12 @@ ftcrfont_draw (struct glyph_string *s,
if (with_background)
{
#ifndef USE_BE_CAIRO
+#ifdef HAVE_X_WINDOWS
x_set_cr_source_with_gc_background (f, s->gc);
#else
+ pgtk_set_cr_source_with_color (f, s->xgcv.background);
+#endif
+#else
struct face *face = s->face;
uint32_t col = s->hl == DRAW_CURSOR ?
@@ -579,8 +589,12 @@ ftcrfont_draw (struct glyph_string *s,
NULL));
}
#ifndef USE_BE_CAIRO
+#ifdef HAVE_X_WINDOWS
x_set_cr_source_with_gc_foreground (f, s->gc);
#else
+ pgtk_set_cr_source_with_color (f, s->xgcv.foreground);
+#endif
+#else
uint32_t col = s->hl == DRAW_CURSOR ?
FRAME_OUTPUT_DATA (s->f)->cursor_fg : face->foreground;
@@ -591,8 +605,12 @@ ftcrfont_draw (struct glyph_string *s,
cairo_set_scaled_font (cr, ftcrfont_info->cr_scaled_font);
cairo_show_glyphs (cr, glyphs, len);
#ifndef USE_BE_CAIRO
+#ifdef HAVE_X_WINDOWS
x_end_cr_clip (f);
#else
+ pgtk_end_cr_clip (f);
+#endif
+#else
haiku_end_cr_clip (cr);
EmacsWindow_end_cr_critical_section (FRAME_HAIKU_WINDOW (f));
BView_draw_unlock (FRAME_HAIKU_VIEW (f));
diff --git a/src/ftfont.h b/src/ftfont.h
index 0e0bebb6f6c..cfab8d3154f 100644
--- a/src/ftfont.h
+++ b/src/ftfont.h
@@ -25,6 +25,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_SIZES_H
+#include FT_TRUETYPE_TABLES_H
#ifdef FT_BDF_H
# include FT_BDF_H
#endif
diff --git a/src/gtkutil.c b/src/gtkutil.c
index 8f8db4ed372..5271ccfba61 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -37,7 +37,14 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "dispextern.h"
#include "frame.h"
#include "systime.h"
+#ifndef HAVE_PGTK
#include "xterm.h"
+#define xp x
+typedef struct x_output xp_output;
+#else
+#define xp pgtk
+typedef struct pgtk_output xp_output;
+#endif
#include "blockinput.h"
#include "window.h"
#include "gtkutil.h"
@@ -56,7 +63,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#endif
#ifdef HAVE_GTK3
+#ifndef HAVE_PGTK
#include <gtk/gtkx.h>
+#endif
#include "emacsgtkfixed.h"
#endif
@@ -131,6 +140,7 @@ static GdkDisplay *gdpy_def;
static void
xg_set_screen (GtkWidget *w, struct frame *f)
{
+#ifndef HAVE_PGTK
if (FRAME_X_DISPLAY (f) != DEFAULT_GDK_DISPLAY ())
{
GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
@@ -141,6 +151,17 @@ xg_set_screen (GtkWidget *w, struct frame *f)
else
gtk_window_set_screen (GTK_WINDOW (w), gscreen);
}
+#else
+ if (FRAME_X_DISPLAY (f) != DEFAULT_GDK_DISPLAY ())
+ {
+ GdkScreen *gscreen = gdk_display_get_default_screen (FRAME_X_DISPLAY (f));
+
+ if (GTK_IS_MENU (w))
+ gtk_menu_set_screen (GTK_MENU (w), gscreen);
+ else
+ gtk_window_set_screen (GTK_WINDOW (w), gscreen);
+ }
+#endif
}
@@ -152,12 +173,20 @@ xg_set_screen (GtkWidget *w, struct frame *f)
multiple displays. */
void
+#ifndef HAVE_PGTK
xg_display_open (char *display_name, Display **dpy)
+#else
+xg_display_open (char *display_name, GdkDisplay **dpy)
+#endif
{
GdkDisplay *gdpy;
unrequest_sigio (); /* See comment in x_display_ok, xterm.c. */
+#ifndef HAVE_PGTK
gdpy = gdk_display_open (display_name);
+#else
+ gdpy = gdk_display_open (strlen (display_name) == 0 ? NULL : display_name);
+#endif
request_sigio ();
if (!gdpy_def && gdpy)
{
@@ -166,7 +195,11 @@ xg_display_open (char *display_name, Display **dpy)
gdpy);
}
+#ifndef HAVE_PGTK
*dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
+#else
+ *dpy = gdpy;
+#endif
}
/* Scaling/HiDPI functions. */
@@ -188,6 +221,9 @@ xg_get_gdk_scale (void)
int
xg_get_scale (struct frame *f)
{
+#ifdef HAVE_PGTK
+ return 1;
+#endif
#ifdef HAVE_GTK3
if (FRAME_GTK_WIDGET (f))
return gtk_widget_get_scale_factor (FRAME_GTK_WIDGET (f));
@@ -198,8 +234,13 @@ xg_get_scale (struct frame *f)
/* Close display DPY. */
void
+#ifndef HAVE_PGTK
xg_display_close (Display *dpy)
+#else
+xg_display_close (GdkDisplay *gdpy)
+#endif
{
+#ifndef HAVE_PGTK
GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
/* If this is the default display, try to change it before closing.
@@ -223,6 +264,31 @@ xg_display_close (Display *dpy)
}
gdk_display_close (gdpy);
+
+#else
+
+ /* If this is the default display, try to change it before closing.
+ If there is no other display to use, gdpy_def is set to NULL, and
+ the next call to xg_display_open resets the default display. */
+ if (gdk_display_get_default () == gdpy)
+ {
+ struct pgtk_display_info *dpyinfo;
+ GdkDisplay *gdpy_new = NULL;
+
+ /* Find another display. */
+ for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
+ if (dpyinfo->gdpy != gdpy)
+ {
+ gdpy_new = dpyinfo->gdpy;
+ gdk_display_manager_set_default_display (gdk_display_manager_get (),
+ gdpy_new);
+ break;
+ }
+ gdpy_def = gdpy_new;
+ }
+
+ gdk_display_close (gdpy);
+#endif
}
@@ -234,12 +300,19 @@ xg_display_close (Display *dpy)
scroll bars on display DPY. */
GdkCursor *
+#ifndef HAVE_PGTK
xg_create_default_cursor (Display *dpy)
+#else
+xg_create_default_cursor (GdkDisplay *gdpy)
+#endif
{
+#ifndef HAVE_PGTK
GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
+#endif
return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
}
+#ifndef HAVE_PGTK
/* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel. */
static GdkPixbuf *
@@ -339,6 +412,8 @@ xg_get_pixbuf_from_surface (cairo_surface_t *surface)
}
#endif /* USE_CAIRO && !HAVE_GTK3 */
+#endif /* !HAVE_PGTK */
+
static Lisp_Object
file_for_image (Lisp_Object image)
{
@@ -609,8 +684,13 @@ xg_check_special_colors (struct frame *f,
block_input ();
{
#ifdef HAVE_GTK3
+#ifndef HAVE_PGTK
GtkStyleContext *gsty
= gtk_widget_get_style_context (FRAME_GTK_OUTER_WIDGET (f));
+#else
+ GtkStyleContext *gsty
+ = gtk_widget_get_style_context (FRAME_WIDGET (f));
+#endif
GdkRGBA col;
char buf[sizeof "rgb://rrrr/gggg/bbbb"];
int state = GTK_STATE_FLAG_SELECTED|GTK_STATE_FLAG_FOCUSED;
@@ -634,9 +714,14 @@ xg_check_special_colors (struct frame *f,
r = col.red * 65535,
g = col.green * 65535,
b = col.blue * 65535;
+#ifndef HAVE_PGTK
sprintf (buf, "rgb:%04x/%04x/%04x", r, g, b);
success_p = x_parse_color (f, buf, color) != 0;
#else
+ sprintf (buf, "#%04x%04x%04x", r, g, b);
+ success_p = pgtk_parse_color (f, buf, color) != 0;
+#endif
+#else
GtkStyle *gsty = gtk_widget_get_style (FRAME_GTK_WIDGET (f));
GdkColor *grgb = get_bg
? &gsty->bg[GTK_STATE_SELECTED]
@@ -659,6 +744,9 @@ xg_check_special_colors (struct frame *f,
/***********************************************************************
Tooltips
***********************************************************************/
+
+#ifndef HAVE_PGTK
+
/* Gtk+ calls this callback when the parent of our tooltip dummy changes.
We use that to pop down the tooltip. This happens if Gtk+ for some
reason wants to change or hide the tooltip. */
@@ -669,7 +757,7 @@ hierarchy_ch_cb (GtkWidget *widget,
gpointer user_data)
{
struct frame *f = user_data;
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
GtkWidget *top = gtk_widget_get_toplevel (x->ttip_lbl);
if (! top || ! GTK_IS_WINDOW (top))
@@ -691,7 +779,7 @@ qttip_cb (GtkWidget *widget,
gpointer user_data)
{
struct frame *f = user_data;
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
if (x->ttip_widget == NULL)
{
GtkWidget *p;
@@ -738,7 +826,7 @@ xg_prepare_tooltip (struct frame *f,
int *width,
int *height)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
GtkWidget *widget;
GdkWindow *gwin;
GdkScreen *screen;
@@ -789,13 +877,19 @@ xg_prepare_tooltip (struct frame *f,
void
xg_show_tooltip (struct frame *f, int root_x, int root_y)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
if (x->ttip_window)
{
block_input ();
+#ifndef HAVE_PGTK
gtk_window_move (x->ttip_window, root_x / xg_get_scale (f),
root_y / xg_get_scale (f));
gtk_widget_show (GTK_WIDGET (x->ttip_window));
+#else
+ gtk_widget_show (GTK_WIDGET (x->ttip_window));
+ gtk_window_move (x->ttip_window, root_x / xg_get_scale (f),
+ root_y / xg_get_scale (f));
+#endif
unblock_input ();
}
}
@@ -807,10 +901,9 @@ xg_show_tooltip (struct frame *f, int root_x, int root_y)
bool
xg_hide_tooltip (struct frame *f)
{
- if (f->output_data.x->ttip_window)
+ if (f->output_data.xp->ttip_window)
{
- GtkWindow *win = f->output_data.x->ttip_window;
-
+ GtkWindow *win = f->output_data.xp->ttip_window;
block_input ();
gtk_widget_hide (GTK_WIDGET (win));
@@ -828,6 +921,30 @@ xg_hide_tooltip (struct frame *f)
return FALSE;
}
+#else
+
+void
+xg_show_tooltip (struct frame *f,
+ Lisp_Object string)
+{
+ Lisp_Object encoded_string = ENCODE_UTF_8 (string);
+ gtk_widget_set_tooltip_text (FRAME_GTK_OUTER_WIDGET (f)
+ ? FRAME_GTK_OUTER_WIDGET (f)
+ : FRAME_GTK_WIDGET (f),
+ SSDATA (encoded_string));
+}
+
+bool
+xg_hide_tooltip (struct frame *f)
+{
+ if (FRAME_GTK_OUTER_WIDGET (f))
+ gtk_widget_set_tooltip_text (FRAME_GTK_OUTER_WIDGET (f), NULL);
+ gtk_widget_set_tooltip_text (FRAME_GTK_WIDGET (f), NULL);
+ return TRUE;
+}
+
+#endif
+
/***********************************************************************
General functions for creating widgets, resizing, events, e.t.c.
@@ -979,8 +1096,23 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
bool was_visible = false;
bool hide_child_frame;
+#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);
@@ -1000,11 +1132,37 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
remain unchanged but giving the frame back its normal size will
be broken ... */
if (EQ (fullscreen, Qfullwidth) && width == FRAME_PIXEL_WIDTH (f))
+#ifndef HAVE_PGTK
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, outer_height);
+ }
+ else
+ {
+ gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
+ gwidth, outer_height);
+ }
+#endif
else if (EQ (fullscreen, Qfullheight) && height == FRAME_PIXEL_HEIGHT (f))
+#ifndef HAVE_PGTK
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)),
+ 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;
@@ -1015,17 +1173,38 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
if (hide_child_frame)
{
block_input ();
+#ifndef HAVE_PGTK
gtk_widget_hide (FRAME_GTK_OUTER_WIDGET (f));
+#else
+ gtk_widget_hide (FRAME_WIDGET (f));
+#endif
unblock_input ();
}
+#ifndef HAVE_PGTK
gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
outer_width, outer_height);
+#else
+ if (FRAME_GTK_OUTER_WIDGET (f))
+ {
+ gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+ outer_width, outer_height);
+ }
+ else
+ {
+ gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
+ outer_width, outer_height);
+ }
+#endif
if (hide_child_frame)
{
block_input ();
+#ifndef HAVE_PGTK
gtk_widget_show_all (FRAME_GTK_OUTER_WIDGET (f));
+#else
+ gtk_widget_show_all (FRAME_WIDGET (f));
+#endif
unblock_input ();
}
@@ -1034,8 +1213,21 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
}
else
{
+#ifndef HAVE_PGTK
gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
outer_width, outer_height);
+#else
+ if (FRAME_GTK_OUTER_WIDGET (f))
+ {
+ gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
+ outer_width, outer_height);
+ }
+ else
+ {
+ gtk_widget_set_size_request (FRAME_GTK_WIDGET (f),
+ outer_width, outer_height);
+ }
+#endif
fullscreen = Qnil;
}
@@ -1060,7 +1252,9 @@ xg_frame_set_char_size (struct frame *f, int width, int height)
/* Must call this to flush out events */
(void)gtk_events_pending ();
gdk_flush ();
+#ifndef HAVE_PGTK
x_wait_for_event (f, ConfigureNotify);
+#endif
if (!NILP (fullscreen))
/* Try to restore fullscreen state. */
@@ -1093,11 +1287,12 @@ xg_height_or_width_changed (struct frame *f)
gtk_window_resize (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
FRAME_TOTAL_PIXEL_WIDTH (f),
FRAME_TOTAL_PIXEL_HEIGHT (f));
- f->output_data.x->hint_flags = 0;
+ f->output_data.xp->hint_flags = 0;
x_wm_set_size_hint (f, 0, 0);
}
#endif
+#ifndef HAVE_PGTK
/* Convert an X Window WSESC on display DPY to its corresponding GtkWidget.
Must be done like this, because GtkWidget:s can have "hidden"
X Window that aren't accessible.
@@ -1125,6 +1320,7 @@ xg_win_to_widget (Display *dpy, Window wdesc)
unblock_input ();
return gwdesc;
}
+#endif
/* Set the background of widget W to PIXEL. */
@@ -1132,9 +1328,18 @@ static void
xg_set_widget_bg (struct frame *f, GtkWidget *w, unsigned long pixel)
{
#ifdef HAVE_GTK3
- XColor xbg;
+ Emacs_Color xbg;
xbg.pixel = pixel;
+#ifndef HAVE_PGTK
if (XQueryColor (FRAME_X_DISPLAY (f), FRAME_X_COLORMAP (f), &xbg))
+#else
+ xbg.red = (pixel >> 16) & 0xff;
+ xbg.green = (pixel >> 8) & 0xff;
+ xbg.blue = (pixel >> 0) & 0xff;
+ xbg.red |= xbg.red << 8;
+ xbg.green |= xbg.green << 8;
+ xbg.blue |= xbg.blue << 8;
+#endif
{
const char format[] = "* { background-color: #%02x%02x%02x; }";
/* The format is always longer than the resulting string. */
@@ -1169,7 +1374,16 @@ style_changed_cb (GObject *go,
struct input_event event;
GdkDisplay *gdpy = user_data;
const char *display_name = gdk_display_get_name (gdpy);
+#ifndef HAVE_PGTK
Display *dpy = GDK_DISPLAY_XDISPLAY (gdpy);
+#else
+ GdkDisplay *dpy = gdpy;
+#endif
+
+#ifndef HAVE_PGTK
+ if (display_name == NULL)
+ display_name = "";
+#endif
EVENT_INIT (event);
event.kind = CONFIG_CHANGED_EVENT;
@@ -1190,7 +1404,11 @@ style_changed_cb (GObject *go,
{
struct frame *f = XFRAME (frame);
if (FRAME_LIVE_P (f)
+#ifndef HAVE_PGTK
&& FRAME_X_P (f)
+#else
+ && FRAME_PGTK_P (f)
+#endif
&& FRAME_X_DISPLAY (f) == dpy)
{
FRAME_TERMINAL (f)->set_scroll_bar_default_width_hook (f);
@@ -1204,6 +1422,7 @@ style_changed_cb (GObject *go,
/* Called when a delete-event occurs on WIDGET. */
+#ifndef HAVE_PGTK
static gboolean
delete_cb (GtkWidget *widget,
GdkEvent *event,
@@ -1211,6 +1430,7 @@ delete_cb (GtkWidget *widget,
{
return TRUE;
}
+#endif
/* Create and set up the GTK widgets for frame F.
Return true if creation succeeded. */
@@ -1224,17 +1444,27 @@ xg_create_frame_widgets (struct frame *f)
#ifndef HAVE_GTK3
GtkRcStyle *style;
#endif
+ GtkWindowType type = GTK_WINDOW_TOPLEVEL;
char *title = 0;
block_input ();
+#ifndef HAVE_PGTK /* gtk_plug not found. */
if (FRAME_X_EMBEDDED_P (f))
{
GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
- wtop = gtk_plug_new_for_display (gdpy, f->output_data.x->parent_desc);
+ wtop = gtk_plug_new_for_display (gdpy, f->output_data.xp->parent_desc);
}
else
- wtop = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ wtop = gtk_window_new (type);
+#else
+ if (f->tooltip)
+ {
+ type = GTK_WINDOW_POPUP;
+ }
+ wtop = gtk_window_new (type);
+ gtk_widget_add_events (wtop, GDK_ALL_EVENTS_MASK);
+#endif
/* gtk_window_set_has_resize_grip is a Gtk+ 3.0 function but Ubuntu
has backported it to Gtk+ 2.0 and they add the resize grip for
@@ -1291,8 +1521,8 @@ xg_create_frame_widgets (struct frame *f)
FRAME_GTK_OUTER_WIDGET (f) = wtop;
FRAME_GTK_WIDGET (f) = wfixed;
- f->output_data.x->vbox_widget = wvbox;
- f->output_data.x->hbox_widget = whbox;
+ f->output_data.xp->vbox_widget = wvbox;
+ f->output_data.xp->hbox_widget = whbox;
gtk_widget_set_has_window (wfixed, TRUE);
@@ -1311,7 +1541,10 @@ xg_create_frame_widgets (struct frame *f)
FIXME: gtk_widget_set_double_buffered is deprecated and might stop
working in the future. We need to migrate away from combining
X and GTK+ drawing to a pure GTK+ build. */
+
+#ifndef HAVE_PGTK
gtk_widget_set_double_buffered (wfixed, FALSE);
+#endif
#if ! GTK_CHECK_VERSION (3, 22, 0)
gtk_window_set_wmclass (GTK_WINDOW (wtop),
@@ -1319,10 +1552,12 @@ xg_create_frame_widgets (struct frame *f)
SSDATA (Vx_resource_class));
#endif
+#ifndef HAVE_PGTK
/* Add callback to do nothing on WM_DELETE_WINDOW. The default in
GTK is to destroy the widget. We want Emacs to do that instead. */
g_signal_connect (G_OBJECT (wtop), "delete-event",
G_CALLBACK (delete_cb), f);
+#endif
/* Convert our geometry parameters into a geometry string
and specify it.
@@ -1333,7 +1568,9 @@ xg_create_frame_widgets (struct frame *f)
gtk_widget_add_events (wfixed,
GDK_POINTER_MOTION_MASK
+#ifndef HAVE_PGTK
| GDK_EXPOSURE_MASK
+#endif
| GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
| GDK_KEY_PRESS_MASK
@@ -1341,13 +1578,19 @@ xg_create_frame_widgets (struct frame *f)
| GDK_LEAVE_NOTIFY_MASK
| GDK_FOCUS_CHANGE_MASK
| GDK_STRUCTURE_MASK
+#ifdef HAVE_PGTK
+ | GDK_SCROLL_MASK
+ | GDK_SMOOTH_SCROLL_MASK
+#endif
| GDK_VISIBILITY_NOTIFY_MASK);
+#ifndef HAVE_PGTK
/* Must realize the windows so the X window gets created. It is used
by callers of this function. */
gtk_widget_realize (wfixed);
FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
initial_set_up_x_back_buffer (f);
+#endif
/* Since GTK clears its window by filling with the background color,
we must keep X and GTK background in sync. */
@@ -1364,6 +1607,9 @@ xg_create_frame_widgets (struct frame *f)
gtk_widget_modify_style (wfixed, style);
#else
gtk_widget_set_can_focus (wfixed, TRUE);
+#ifdef HAVE_PGTK
+ gtk_widget_grab_focus (wfixed);
+#endif
gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE);
#endif
@@ -1376,11 +1622,13 @@ xg_create_frame_widgets (struct frame *f)
}
/* Steal a tool tip window we can move ourselves. */
- f->output_data.x->ttip_widget = 0;
- f->output_data.x->ttip_lbl = 0;
- f->output_data.x->ttip_window = 0;
+ f->output_data.xp->ttip_widget = 0;
+ f->output_data.xp->ttip_lbl = 0;
+ f->output_data.xp->ttip_window = 0;
+#ifndef HAVE_PGTK
gtk_widget_set_tooltip_text (wtop, "Dummy text");
g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f);
+#endif
{
GdkScreen *screen = gtk_widget_get_screen (wtop);
@@ -1403,12 +1651,114 @@ xg_create_frame_widgets (struct frame *f)
return 1;
}
+#ifdef HAVE_PGTK
+void
+xg_create_frame_outer_widgets (struct frame *f)
+{
+ GtkWidget *wtop;
+ GtkWidget *wvbox, *whbox;
+ GtkWindowType type = GTK_WINDOW_TOPLEVEL;
+ char *title = 0;
+
+ block_input ();
+
+ wtop = gtk_window_new (type);
+ gtk_widget_add_events (wtop, GDK_ALL_EVENTS_MASK);
+
+ xg_set_screen (wtop, f);
+
+ wvbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ whbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_set_homogeneous (GTK_BOX (wvbox), FALSE);
+ gtk_box_set_homogeneous (GTK_BOX (whbox), FALSE);
+
+ /* Use same names as the Xt port does. I.e. Emacs.pane.emacs by default */
+ gtk_widget_set_name (wtop, EMACS_CLASS);
+ gtk_widget_set_name (wvbox, "pane");
+
+ /* If this frame has a title or name, set it in the title bar. */
+ if (! NILP (f->title))
+ title = SSDATA (ENCODE_UTF_8 (f->title));
+ else if (! NILP (f->name))
+ title = SSDATA (ENCODE_UTF_8 (f->name));
+
+ if (title)
+ gtk_window_set_title (GTK_WINDOW (wtop), title);
+
+ if (FRAME_UNDECORATED (f))
+ {
+ gtk_window_set_decorated (GTK_WINDOW (wtop), FALSE);
+ store_frame_param (f, Qundecorated, Qt);
+ }
+
+ FRAME_GTK_OUTER_WIDGET (f) = wtop;
+ f->output_data.xp->vbox_widget = wvbox;
+ f->output_data.xp->hbox_widget = whbox;
+
+ gtk_container_add (GTK_CONTAINER (wtop), wvbox);
+ gtk_box_pack_start (GTK_BOX (wvbox), whbox, TRUE, TRUE, 0);
+
+ if (FRAME_EXTERNAL_TOOL_BAR (f))
+ update_frame_tool_bar (f);
+
+#if ! GTK_CHECK_VERSION (3, 22, 0)
+ gtk_window_set_wmclass (GTK_WINDOW (wtop),
+ SSDATA (Vx_resource_name),
+ SSDATA (Vx_resource_class));
+#endif
+
+ /* Convert our geometry parameters into a geometry string
+ and specify it.
+ GTK will itself handle calculating the real position this way. */
+ xg_set_geometry (f);
+ f->win_gravity
+ = gtk_window_get_gravity (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
+
+ gtk_window_set_resizable (GTK_WINDOW (wtop), TRUE);
+
+ if (FRAME_OVERRIDE_REDIRECT (f))
+ {
+ GdkWindow *gwin = gtk_widget_get_window (wtop);
+
+ if (gwin)
+ gdk_window_set_override_redirect (gwin, TRUE);
+ }
+
+ /* Steal a tool tip window we can move ourselves. */
+ f->output_data.xp->ttip_widget = 0;
+ f->output_data.xp->ttip_lbl = 0;
+ f->output_data.xp->ttip_window = 0;
+#ifndef HAVE_PGTK
+ gtk_widget_set_tooltip_text (wtop, "Dummy text");
+ g_signal_connect (wtop, "query-tooltip", G_CALLBACK (qttip_cb), f);
+#endif
+
+ {
+ GdkScreen *screen = gtk_widget_get_screen (wtop);
+ GtkSettings *gs = gtk_settings_get_for_screen (screen);
+ /* Only connect this signal once per screen. */
+ if (! g_signal_handler_find (G_OBJECT (gs),
+ G_SIGNAL_MATCH_FUNC,
+ 0, 0, 0,
+ (gpointer) G_CALLBACK (style_changed_cb),
+ 0))
+ {
+ g_signal_connect (G_OBJECT (gs), "notify::gtk-theme-name",
+ G_CALLBACK (style_changed_cb),
+ gdk_screen_get_display (screen));
+ }
+ }
+
+ unblock_input ();
+}
+#endif
+
void
xg_free_frame_widgets (struct frame *f)
{
if (FRAME_GTK_OUTER_WIDGET (f))
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
struct xg_frame_tb_info *tbinfo
= g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
TB_INFO_KEY);
@@ -1416,10 +1766,14 @@ xg_free_frame_widgets (struct frame *f)
xfree (tbinfo);
/* x_free_frame_resources should have taken care of it */
+#ifndef HAVE_PGTK
eassert (!FRAME_X_DOUBLE_BUFFERED_P (f));
+#endif
gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
+#ifndef HAVE_PGTK
FRAME_X_RAW_DRAWABLE (f) = 0;
+#endif
FRAME_GTK_OUTER_WIDGET (f) = 0;
if (x->ttip_widget)
{
@@ -1461,9 +1815,12 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
XSETFRAME (frame, f);
fs_state = Fframe_parameter (frame, Qfullscreen);
- if ((EQ (fs_state, Qmaximized) || EQ (fs_state, Qfullboth)) &&
- (x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state) ||
- x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state_fullscreen)))
+ if ((EQ (fs_state, Qmaximized) || EQ (fs_state, Qfullboth))
+#ifndef HAVE_PGTK
+ && (x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state) ||
+ x_wm_supports (f, FRAME_DISPLAY_INFO (f)->Xatom_net_wm_state_fullscreen))
+#endif
+ )
{
/* Don't set hints when maximized or fullscreen. Apparently KWin and
Gtk3 don't get along and the frame shrinks (!).
@@ -1474,14 +1831,14 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
if (flags)
{
memset (&size_hints, 0, sizeof (size_hints));
- f->output_data.x->size_hints = size_hints;
- f->output_data.x->hint_flags = hint_flags;
+ f->output_data.xp->size_hints = size_hints;
+ f->output_data.xp->hint_flags = hint_flags;
}
else
flags = f->size_hint_flags;
- size_hints = f->output_data.x->size_hints;
- hint_flags = f->output_data.x->hint_flags;
+ size_hints = f->output_data.xp->size_hints;
+ hint_flags = f->output_data.xp->hint_flags;
hint_flags |= GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE;
size_hints.width_inc = frame_resize_pixelwise ? 1 : FRAME_COLUMN_WIDTH (f);
@@ -1543,16 +1900,16 @@ x_wm_set_size_hint (struct frame *f, long int flags, bool user_position)
size_hints.width_inc /= scale;
size_hints.height_inc /= scale;
- if (hint_flags != f->output_data.x->hint_flags
+ if (hint_flags != f->output_data.xp->hint_flags
|| memcmp (&size_hints,
- &f->output_data.x->size_hints,
+ &f->output_data.xp->size_hints,
sizeof (size_hints)) != 0)
{
block_input ();
gtk_window_set_geometry_hints (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
NULL, &size_hints, hint_flags);
- f->output_data.x->size_hints = size_hints;
- f->output_data.x->hint_flags = hint_flags;
+ f->output_data.xp->size_hints = size_hints;
+ f->output_data.xp->hint_flags = hint_flags;
unblock_input ();
}
}
@@ -1592,6 +1949,10 @@ xg_set_background_color (struct frame *f, unsigned long bg)
void
xg_set_undecorated (struct frame *f, Lisp_Object undecorated)
{
+#ifdef HAVE_PGTK
+ if (!FRAME_GTK_OUTER_WIDGET (f))
+ return;
+#endif
if (FRAME_GTK_WIDGET (f))
{
block_input ();
@@ -1618,7 +1979,11 @@ xg_frame_restack (struct frame *f1, struct frame *f2, bool above_flag)
XSETFRAME (frame2, f2);
gdk_window_restack (gwin1, gwin2, above_flag);
+#ifndef HAVE_PGTK
x_sync (f1);
+#else
+ gdk_flush ();
+#endif
}
unblock_input ();
}
@@ -1629,10 +1994,17 @@ void
xg_set_skip_taskbar (struct frame *f, Lisp_Object skip_taskbar)
{
block_input ();
+#ifndef HAVE_PGTK
if (FRAME_GTK_WIDGET (f))
gdk_window_set_skip_taskbar_hint
(gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f)),
NILP (skip_taskbar) ? FALSE : TRUE);
+#else
+ if (FRAME_GTK_OUTER_WIDGET (f))
+ gdk_window_set_skip_taskbar_hint
+ (gtk_widget_get_window (FRAME_GTK_OUTER_WIDGET (f)),
+ NILP (skip_taskbar) ? FALSE : TRUE);
+#endif
unblock_input ();
}
@@ -1641,6 +2013,10 @@ xg_set_skip_taskbar (struct frame *f, Lisp_Object skip_taskbar)
void
xg_set_no_focus_on_map (struct frame *f, Lisp_Object no_focus_on_map)
{
+#ifdef HAVE_PGTK
+ if (!FRAME_GTK_OUTER_WIDGET (f))
+ return;
+#endif
block_input ();
if (FRAME_GTK_WIDGET (f))
{
@@ -1656,12 +2032,19 @@ xg_set_no_focus_on_map (struct frame *f, Lisp_Object no_focus_on_map)
void
xg_set_no_accept_focus (struct frame *f, Lisp_Object no_accept_focus)
{
+ gboolean g_no_accept_focus = NILP (no_accept_focus) ? TRUE : FALSE;
+#ifdef HAVE_PGTK
+ if (!FRAME_GTK_OUTER_WIDGET (f))
+ {
+ if (FRAME_WIDGET (f))
+ gtk_widget_set_can_focus (FRAME_WIDGET (f), g_no_accept_focus);
+ return;
+ }
+#endif
block_input ();
if (FRAME_GTK_WIDGET (f))
{
GtkWindow *gwin = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
- gboolean g_no_accept_focus = NILP (no_accept_focus) ? TRUE : FALSE;
-
gtk_window_set_accept_focus (gwin, g_no_accept_focus);
}
unblock_input ();
@@ -1682,18 +2065,24 @@ xg_set_override_redirect (struct frame *f, Lisp_Object override_redirect)
unblock_input ();
}
+#ifndef HAVE_PGTK
/* Set the frame icon to ICON_PIXMAP/MASK. This must be done with GTK
functions so GTK does not overwrite the icon. */
void
xg_set_frame_icon (struct frame *f, Pixmap icon_pixmap, Pixmap icon_mask)
{
+#ifdef HAVE_PGTK
+ if (!FRAME_GTK_OUTER_WIDGET (f))
+ return;
+#endif
GdkPixbuf *gp = xg_get_pixbuf_from_pix_and_mask (f,
icon_pixmap,
icon_mask);
if (gp)
gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), gp);
}
+#endif
@@ -2230,6 +2619,11 @@ xg_get_file_name (struct frame *f,
int filesel_done = 0;
xg_get_file_func func;
+#ifdef HAVE_PGTK
+ if (!FRAME_GTK_OUTER_WIDGET (f))
+ error ("Can't open dialog from child frames");
+#endif
+
#ifdef HAVE_GTK_FILE_SELECTION_NEW
if (xg_uses_old_file_dialog ())
@@ -2327,6 +2721,11 @@ xg_get_font (struct frame *f, const char *default_name)
int done = 0;
Lisp_Object font = Qnil;
+#ifdef HAVE_PGTK
+ if (!FRAME_GTK_OUTER_WIDGET (f))
+ error ("Can't open dialog from child frames");
+#endif
+
w = gtk_font_chooser_dialog_new
("Pick a font", GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
@@ -2524,7 +2923,7 @@ xg_mark_data (void)
{
struct frame *f = XFRAME (frame);
- if (FRAME_X_P (f) && FRAME_GTK_OUTER_WIDGET (f))
+ if ((FRAME_X_P (f) || FRAME_PGTK_P (f)) && FRAME_GTK_OUTER_WIDGET (f))
{
struct xg_frame_tb_info *tbinfo
= g_object_get_data (G_OBJECT (FRAME_GTK_OUTER_WIDGET (f)),
@@ -2688,6 +3087,11 @@ make_menu_item (const char *utf8_label,
if (wtoadd) gtk_container_add (GTK_CONTAINER (w), wtoadd);
if (! item->enabled) gtk_widget_set_sensitive (w, FALSE);
+#ifdef HAVE_PGTK
+ if (!NILP (item->help))
+ gtk_widget_set_tooltip_text (w, SSDATA (item->help));
+#endif
+
return w;
}
@@ -2754,6 +3158,20 @@ xg_create_one_menuitem (widget_value *item,
return w;
}
+#ifdef HAVE_PGTK
+static gboolean
+menu_bar_button_pressed_cb (GtkWidget *widget, GdkEvent *event,
+ gpointer user_data)
+{
+ struct frame *f = user_data;
+
+ if (event->button.button < 4)
+ set_frame_menubar (f, true);
+
+ return false;
+}
+#endif
+
/* Create a full menu tree specified by DATA.
F is the frame the created menu belongs to.
SELECT_CB is the callback to use when a menu item is selected.
@@ -2811,6 +3229,10 @@ create_menus (widget_value *data,
else
{
wmenu = gtk_menu_bar_new ();
+#ifdef HAVE_PGTK
+ g_signal_connect (G_OBJECT (wmenu), "button-press-event",
+ G_CALLBACK (menu_bar_button_pressed_cb), f);
+#endif
/* Set width of menu bar to a small value so it doesn't enlarge
a small initial frame size. The width will be set to the
width of the frame later on when it is added to a container.
@@ -2827,9 +3249,15 @@ create_menus (widget_value *data,
if (name)
gtk_widget_set_name (wmenu, name);
+#ifndef HAVE_PGTK
if (deactivate_cb)
g_signal_connect (G_OBJECT (wmenu),
"selection-done", deactivate_cb, 0);
+#else
+ if (deactivate_cb)
+ g_signal_connect (G_OBJECT (wmenu),
+ "deactivate", deactivate_cb, 0);
+#endif
}
for (item = data; item; item = item->next)
@@ -3551,7 +3979,7 @@ menubar_map_cb (GtkWidget *w, gpointer user_data)
void
xg_update_frame_menubar (struct frame *f)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
GtkRequisition req;
if (!x->menubar_widget || gtk_widget_get_mapped (x->menubar_widget))
@@ -3584,7 +4012,7 @@ xg_update_frame_menubar (struct frame *f)
void
free_frame_menubar (struct frame *f)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
if (x->menubar_widget)
{
@@ -3600,6 +4028,7 @@ free_frame_menubar (struct frame *f)
}
}
+#ifndef HAVE_PGTK
bool
xg_event_is_for_menubar (struct frame *f, const XEvent *event)
{
@@ -3667,6 +4096,7 @@ xg_event_is_for_menubar (struct frame *f, const XEvent *event)
g_list_free (list);
return iter != 0;
}
+#endif
@@ -3822,6 +4252,7 @@ xg_get_default_scrollbar_height (struct frame *f)
return scroll_bar_width_for_theme * xg_get_scale (f);
}
+#ifndef HAVE_PGTK
/* Return the scrollbar id for X Window WID on display DPY.
Return -1 if WID not in id_to_widget. */
@@ -3842,6 +4273,7 @@ xg_get_scroll_id_for_window (Display *dpy, Window wid)
return -1;
}
+#endif
/* Callback invoked when scroll bar WIDGET is destroyed.
DATA is the index into id_to_widget for WIDGET.
@@ -3891,7 +4323,7 @@ xg_finish_scroll_bar_creation (struct frame *f,
also, which causes flicker. Put an event box between the edit widget
and the scroll bar, so the scroll bar instead draws itself on the
event box window. */
- gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
+ gtk_fixed_put (GTK_FIXED (f->output_data.xp->edit_widget), webox, -1, -1);
gtk_container_add (GTK_CONTAINER (webox), wscroll);
xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f));
@@ -3901,11 +4333,28 @@ xg_finish_scroll_bar_creation (struct frame *f,
real X window, it and its scroll-bar child try to draw on the
Emacs main window, which we draw over using Xlib. */
gtk_widget_realize (webox);
+#ifdef HAVE_PGTK
+ gtk_widget_show_all (webox);
+#endif
+#ifndef HAVE_PGTK
GTK_WIDGET_TO_X_WIN (webox);
+#endif
/* Set the cursor to an arrow. */
xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
+#ifdef HAVE_PGTK
+ GtkStyleContext *ctxt = gtk_widget_get_style_context (wscroll);
+ gtk_style_context_add_provider (ctxt,
+ GTK_STYLE_PROVIDER (FRAME_OUTPUT_DATA (f)->
+ scrollbar_foreground_css_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+ gtk_style_context_add_provider (ctxt,
+ GTK_STYLE_PROVIDER (FRAME_OUTPUT_DATA (f)->
+ scrollbar_background_css_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+#endif
+
bar->x_window = scroll_id;
}
@@ -4006,7 +4455,7 @@ xg_update_scrollbar_pos (struct frame *f,
GtkWidget *wscroll = xg_get_widget_from_map (scrollbar_id);
if (wscroll)
{
- GtkWidget *wfixed = f->output_data.x->edit_widget;
+ GtkWidget *wfixed = f->output_data.xp->edit_widget;
GtkWidget *wparent = gtk_widget_get_parent (wscroll);
gint msl;
int scale = xg_get_scale (f);
@@ -4046,7 +4495,11 @@ xg_update_scrollbar_pos (struct frame *f,
/* Clear under old scroll bar position. */
oldw += (scale - 1) * oldw;
oldx -= (scale - 1) * oldw;
+#ifndef HAVE_PGTK
x_clear_area (f, oldx, oldy, oldw, oldh);
+#else
+ pgtk_clear_area (f, oldx, oldy, oldw, oldh);
+#endif
}
if (!hidden)
@@ -4054,15 +4507,23 @@ xg_update_scrollbar_pos (struct frame *f,
GtkWidget *scrollbar = xg_get_widget_from_map (scrollbar_id);
GtkWidget *webox = gtk_widget_get_parent (scrollbar);
+#ifndef HAVE_PGTK
/* Don't obscure any child frames. */
XLowerWindow (FRAME_X_DISPLAY (f), GTK_WIDGET_TO_X_WIN (webox));
+#else
+ gdk_window_lower (gtk_widget_get_window (webox));
+#endif
}
/* GTK does not redraw until the main loop is entered again, but
if there are no X events pending we will not enter it. So we sync
here to get some events. */
+#ifndef HAVE_PGTK
x_sync (f);
+#else
+ gdk_flush ();
+#endif
SET_FRAME_GARBAGED (f);
cancel_mouse_face (f);
}
@@ -4087,7 +4548,7 @@ xg_update_horizontal_scrollbar_pos (struct frame *f,
if (wscroll)
{
- GtkWidget *wfixed = f->output_data.x->edit_widget;
+ GtkWidget *wfixed = f->output_data.xp->edit_widget;
GtkWidget *wparent = gtk_widget_get_parent (wscroll);
gint msl;
int scale = xg_get_scale (f);
@@ -4123,7 +4584,11 @@ xg_update_horizontal_scrollbar_pos (struct frame *f,
}
if (oldx != -1 && oldw > 0 && oldh > 0)
/* Clear under old scroll bar position. */
+#ifndef HAVE_PGTK
x_clear_area (f, oldx, oldy, oldw, oldh);
+#else
+ pgtk_clear_area (f, oldx, oldy, oldw, oldh);
+#endif
/* GTK does not redraw until the main loop is entered again, but
if there are no X events pending we will not enter it. So we sync
@@ -4134,11 +4599,19 @@ xg_update_horizontal_scrollbar_pos (struct frame *f,
xg_get_widget_from_map (scrollbar_id);
GtkWidget *webox = gtk_widget_get_parent (scrollbar);
+#ifndef HAVE_PGTK
/* Don't obscure any child frames. */
XLowerWindow (FRAME_X_DISPLAY (f), GTK_WIDGET_TO_X_WIN (webox));
+#else
+ gdk_window_lower (gtk_widget_get_window (webox));
+#endif
}
+#ifndef HAVE_PGTK
x_sync (f);
+#else
+ gdk_flush ();
+#endif
SET_FRAME_GARBAGED (f);
cancel_mouse_face (f);
}
@@ -4282,7 +4755,7 @@ xg_set_toolkit_horizontal_scroll_bar_thumb (struct scroll_bar *bar,
frame. This function does additional checks. */
bool
-xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
+xg_event_is_for_scrollbar (struct frame *f, const EVENT *event)
{
bool retval = 0;
@@ -4298,11 +4771,21 @@ xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
|| (event->type == ButtonPress
&& event->xbutton.button < 4)))
#else
- if (f && event->type == ButtonPress && event->xbutton.button < 4)
+ if (f
+#ifndef HAVE_PGTK
+ && event->type == ButtonPress && event->xbutton.button < 4
+#else
+ && event->type == GDK_BUTTON_PRESS && event->button.button < 4
+#endif
+ )
#endif /* HAVE_XINPUT2 */
{
/* Check if press occurred outside the edit widget. */
+#ifndef HAVE_PGTK
GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
+#else
+ GdkDisplay *gdpy = FRAME_X_DISPLAY (f);
+#endif
GdkWindow *gwin;
#ifdef HAVE_GTK3
#if GTK_CHECK_VERSION (3, 20, 0)
@@ -4316,7 +4799,7 @@ xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
#else
gwin = gdk_display_get_window_at_pointer (gdpy, NULL, NULL);
#endif
- retval = gwin != gtk_widget_get_window (f->output_data.x->edit_widget);
+ retval = gwin != gtk_widget_get_window (f->output_data.xp->edit_widget);
#ifdef HAVE_XINPUT2
GtkWidget *grab = gtk_grab_get_current ();
if (event->type == GenericEvent
@@ -4337,8 +4820,14 @@ xg_event_is_for_scrollbar (struct frame *f, const XEvent *event)
|| event->type == MotionNotify)))
#else
else if (f
+#ifndef HAVE_PGTK
&& ((event->type == ButtonRelease && event->xbutton.button < 4)
- || event->type == MotionNotify))
+ || event->type == MotionNotify)
+#else
+ && ((event->type == GDK_BUTTON_RELEASE && event->button.button < 4)
+ || event->type == GDK_MOTION_NOTIFY)
+#endif
+ )
#endif /* HAVE_XINPUT2 */
{
/* If we are releasing or moving the scroll bar, it has the grab. */
@@ -4417,7 +4906,11 @@ draw_page (GtkPrintOperation *operation, GtkPrintContext *context,
struct frame *f = XFRAME (Fnth (make_fixnum (page_nr), frames));
cairo_t *cr = gtk_print_context_get_cairo_context (context);
+#ifndef HAVE_PGTK
x_cr_draw_frame (cr, f);
+#else
+ pgtk_cr_draw_frame (cr, f);
+#endif
}
void
@@ -4518,7 +5011,11 @@ xg_tool_bar_callback (GtkWidget *w, gpointer client_data)
/* Convert between the modifier bits GDK uses and the modifier bits
Emacs uses. This assumes GDK and X masks are the same, which they are when
this is written. */
+#ifndef HAVE_PGTK
event.modifiers = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), mod);
+#else
+ event.modifiers = pgtk_gtk_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), mod);
+#endif
kbd_buffer_store_event (&event);
/* Return focus to the frame after we have clicked on a detached
@@ -4615,7 +5112,7 @@ xg_tool_bar_item_expose_callback (GtkWidget *w,
static void
xg_pack_tool_bar (struct frame *f, Lisp_Object pos)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
bool into_hbox = EQ (pos, Qleft) || EQ (pos, Qright);
GtkWidget *top_widget = x->toolbar_widget;
@@ -4671,7 +5168,7 @@ tb_size_cb (GtkWidget *widget,
static void
xg_create_tool_bar (struct frame *f)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
#ifdef HAVE_GTK3
GtkStyleContext *gsty;
#endif
@@ -4910,7 +5407,7 @@ xg_tool_item_stale_p (GtkWidget *wbutton, const char *stock_name,
static bool
xg_update_tool_bar_sizes (struct frame *f)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
GtkRequisition req;
int nl = 0, nr = 0, nt = 0, nb = 0;
GtkWidget *top_widget = x->toolbar_widget;
@@ -4996,7 +5493,7 @@ void
update_frame_tool_bar (struct frame *f)
{
int i, j;
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
int hmargin = 0, vmargin = 0;
GtkToolbar *wtoolbar;
GtkToolItem *ti;
@@ -5011,6 +5508,11 @@ update_frame_tool_bar (struct frame *f)
if (! FRAME_GTK_WIDGET (f))
return;
+#ifdef HAVE_PGTK
+ if (! FRAME_GTK_OUTER_WIDGET (f))
+ return;
+#endif
+
block_input ();
if (RANGED_FIXNUMP (1, Vtool_bar_button_margin, INT_MAX))
@@ -5306,7 +5808,7 @@ update_frame_tool_bar (struct frame *f)
void
free_frame_tool_bar (struct frame *f)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
if (x->toolbar_widget)
{
@@ -5351,7 +5853,7 @@ free_frame_tool_bar (struct frame *f)
void
xg_change_toolbar_position (struct frame *f, Lisp_Object pos)
{
- struct x_output *x = f->output_data.x;
+ xp_output *x = f->output_data.xp;
GtkWidget *top_widget = x->toolbar_widget;
if (! x->toolbar_widget || ! top_widget)
diff --git a/src/gtkutil.h b/src/gtkutil.h
index 95dd75b7fad..9c6160dd772 100644
--- a/src/gtkutil.h
+++ b/src/gtkutil.h
@@ -25,7 +25,13 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <gtk/gtk.h>
#include "../lwlib/lwlib-widget.h"
+#ifdef HAVE_PGTK
+#include "pgtkterm.h"
+#define EVENT GdkEvent
+#else
#include "xterm.h"
+#define EVENT XEvent
+#endif
/* Minimum and maximum values used for GTK scroll bars */
@@ -105,7 +111,7 @@ extern void xg_modify_menubar_widgets (GtkWidget *menubar,
extern void xg_update_frame_menubar (struct frame *f);
-extern bool xg_event_is_for_menubar (struct frame *, const XEvent *);
+extern bool xg_event_is_for_menubar (struct frame *, const EVENT *);
extern ptrdiff_t xg_get_scroll_id_for_window (Display *dpy, Window wid);
@@ -142,7 +148,7 @@ extern void xg_set_toolkit_horizontal_scroll_bar_thumb (struct scroll_bar *bar,
int portion,
int position,
int whole);
-extern bool xg_event_is_for_scrollbar (struct frame *, const XEvent *);
+extern bool xg_event_is_for_scrollbar (struct frame *, const EVENT *);
extern int xg_get_default_scrollbar_width (struct frame *f);
extern int xg_get_default_scrollbar_height (struct frame *f);
@@ -157,9 +163,15 @@ extern void xg_frame_set_char_size (struct frame *f, int width, int height);
extern GtkWidget * xg_win_to_widget (Display *dpy, Window wdesc);
extern int xg_get_scale (struct frame *f);
+#ifndef HAVE_PGTK
extern void xg_display_open (char *display_name, Display **dpy);
extern void xg_display_close (Display *dpy);
extern GdkCursor * xg_create_default_cursor (Display *dpy);
+#else
+extern void xg_display_open (char *display_name, GdkDisplay **dpy);
+extern void xg_display_close (GdkDisplay *gdpy);
+extern GdkCursor * xg_create_default_cursor (GdkDisplay *gdpy);
+#endif
extern bool xg_create_frame_widgets (struct frame *f);
extern void xg_free_frame_widgets (struct frame *f);
@@ -167,10 +179,15 @@ extern void xg_set_background_color (struct frame *f, unsigned long bg);
extern bool xg_check_special_colors (struct frame *f,
const char *color_name,
Emacs_Color *color);
+#ifdef HAVE_PGTK
+extern void xg_create_frame_outer_widgets (struct frame *f);
+#endif
+#ifndef HAVE_PGTK
extern void xg_set_frame_icon (struct frame *f,
Pixmap icon_pixmap,
Pixmap icon_mask);
+#endif
extern void xg_set_undecorated (struct frame *f, Lisp_Object undecorated);
extern void xg_frame_restack (struct frame *f1, struct frame *f2, bool above);
@@ -183,7 +200,11 @@ extern bool xg_prepare_tooltip (struct frame *f,
Lisp_Object string,
int *width,
int *height);
+#ifndef HAVE_PGTK
extern void xg_show_tooltip (struct frame *f, int root_x, int root_y);
+#else
+extern void xg_show_tooltip (struct frame *f, Lisp_Object string);
+#endif
extern bool xg_hide_tooltip (struct frame *f);
#ifdef USE_CAIRO
diff --git a/src/image.c b/src/image.c
index 49b14c49154..55f5f5549eb 100644
--- a/src/image.c
+++ b/src/image.c
@@ -101,6 +101,17 @@ static unsigned long image_alloc_image_color (struct frame *, struct image *,
Lisp_Object, unsigned long);
#endif /* USE_CAIRO */
+#if defined HAVE_PGTK && defined HAVE_IMAGEMAGICK
+/* On pgtk, we don't want to create scaled image.
+ * If we create scaled image on scale=2.0 environment,
+ * the created image is half size and Gdk scales it back,
+ * and the result is blurry.
+ * To avoid this, we hold original size image as far as
+ * we can, and let Gdk to scale it when it is shown.
+ */
+# define DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE
+#endif
+
#ifdef HAVE_NTGUI
/* We need (or want) w32.h only when we're _not_ compiling for Cygwin. */
@@ -131,6 +142,10 @@ typedef struct ns_bitmap_record Bitmap_Record;
#endif /* HAVE_NS */
+#ifdef HAVE_PGTK
+typedef struct pgtk_bitmap_record Bitmap_Record;
+#endif /* HAVE_PGTK */
+
#if (defined HAVE_X_WINDOWS \
&& ! (defined HAVE_NTGUI || defined USE_CAIRO || defined HAVE_NS))
/* W32_TODO : Color tables on W32. */
@@ -419,6 +434,34 @@ image_reference_bitmap (struct frame *f, ptrdiff_t id)
++FRAME_DISPLAY_INFO (f)->bitmaps[id - 1].refcount;
}
+#ifdef HAVE_PGTK
+static cairo_pattern_t *
+image_create_pattern_from_pixbuf (struct frame *f, GdkPixbuf * pixbuf)
+{
+ GdkPixbuf *pb = gdk_pixbuf_add_alpha (pixbuf, TRUE, 255, 255, 255);
+ cairo_surface_t *surface =
+ cairo_surface_create_similar_image (cairo_get_target
+ (f->output_data.pgtk->cr_context),
+ CAIRO_FORMAT_A1,
+ gdk_pixbuf_get_width (pb),
+ gdk_pixbuf_get_height (pb));
+
+ cairo_t *cr = cairo_create (surface);
+ gdk_cairo_set_source_pixbuf (cr, pb, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ cairo_pattern_t *pat = cairo_pattern_create_for_surface (surface);
+ cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
+
+ cairo_surface_destroy (surface);
+ g_object_unref (pb);
+
+ return pat;
+}
+#endif
+
/* Create a bitmap for frame F from a HEIGHT x WIDTH array of bits at BITS. */
ptrdiff_t
@@ -453,6 +496,49 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
return -1;
#endif
+#ifdef HAVE_PGTK
+ GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ FALSE,
+ 8,
+ width,
+ height);
+ {
+ char *sp = bits;
+ int mask = 0x01;
+ unsigned char *buf = gdk_pixbuf_get_pixels (pixbuf);
+ int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ for (int y = 0; y < height; y++)
+ {
+ unsigned char *dp = buf + rowstride * y;
+ for (int x = 0; x < width; x++)
+ {
+ if (*sp & mask)
+ {
+ *dp++ = 0xff;
+ *dp++ = 0xff;
+ *dp++ = 0xff;
+ }
+ else
+ {
+ *dp++ = 0x00;
+ *dp++ = 0x00;
+ *dp++ = 0x00;
+ }
+ if ((mask <<= 1) >= 0x100)
+ {
+ mask = 0x01;
+ sp++;
+ }
+ }
+ if (mask != 0x01)
+ {
+ mask = 0x01;
+ sp++;
+ }
+ }
+ }
+#endif /* HAVE_PGTK */
+
#ifdef HAVE_HAIKU
void *bitmap = BBitmap_new (width, height, 1);
BBitmap_import_mono_bits (bitmap, bits, width, height);
@@ -465,6 +551,13 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
dpyinfo->bitmaps[id - 1].depth = 1;
#endif
+#ifdef HAVE_PGTK
+ dpyinfo->bitmaps[id - 1].img = pixbuf;
+ dpyinfo->bitmaps[id - 1].depth = 1;
+ dpyinfo->bitmaps[id - 1].pattern =
+ image_create_pattern_from_pixbuf (f, pixbuf);
+#endif
+
#ifdef HAVE_HAIKU
dpyinfo->bitmaps[id - 1].img = bitmap;
dpyinfo->bitmaps[id - 1].depth = 1;
@@ -522,6 +615,30 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file)
return id;
#endif
+#ifdef HAVE_PGTK
+ GError *err = NULL;
+ ptrdiff_t id;
+ void * bitmap = gdk_pixbuf_new_from_file (SSDATA (file), &err);
+
+ if (!bitmap)
+ {
+ g_error_free (err);
+ return -1;
+ }
+
+ id = image_allocate_bitmap_record (f);
+
+ dpyinfo->bitmaps[id - 1].img = bitmap;
+ dpyinfo->bitmaps[id - 1].refcount = 1;
+ dpyinfo->bitmaps[id - 1].file = xlispstrdup (file);
+ //dpyinfo->bitmaps[id - 1].depth = 1;
+ dpyinfo->bitmaps[id - 1].height = gdk_pixbuf_get_width (bitmap);
+ dpyinfo->bitmaps[id - 1].width = gdk_pixbuf_get_height (bitmap);
+ dpyinfo->bitmaps[id - 1].pattern
+ = image_create_pattern_from_pixbuf (f, bitmap);
+ return id;
+#endif
+
#ifdef HAVE_X_WINDOWS
unsigned int width, height;
Pixmap bitmap;
@@ -594,6 +711,11 @@ free_bitmap_record (Display_Info *dpyinfo, Bitmap_Record *bm)
ns_release_object (bm->img);
#endif
+#ifdef HAVE_PGTK
+ if (bm->pattern != NULL)
+ cairo_pattern_destroy (bm->pattern);
+#endif
+
#ifdef HAVE_HAIKU
BBitmap_free (bm->img);
#endif
@@ -1358,7 +1480,6 @@ image_ascent (struct image *img, struct face *face, struct glyph_slice *slice)
return ascent;
}
-
/* Image background colors. */
@@ -1382,6 +1503,7 @@ four_corners_best (Emacs_Pix_Context pimg, int *corners,
corner_pixels[3] = GET_PIXEL (pimg, corners[LEFT_CORNER], corners[BOT_CORNER] - 1);
}
else
+
{
/* Get the colors at the corner_pixels of pimg. */
corner_pixels[0] = GET_PIXEL (pimg, 0, 0);
@@ -2250,7 +2372,8 @@ compute_image_rotation (struct image *img, double *rotation)
static void
image_set_transform (struct frame *f, struct image *img)
{
-# ifdef HAVE_IMAGEMAGICK
+# if (defined HAVE_IMAGEMAGICK \
+ && !defined DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE)
/* ImageMagick images already have the correct transform. */
if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
return;
@@ -4097,6 +4220,13 @@ xbm_load (struct frame *f, struct image *img)
XPM images
***********************************************************************/
+#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK)
+
+static bool xpm_image_p (Lisp_Object object);
+static bool xpm_load (struct frame *f, struct image *img);
+
+#endif /* HAVE_XPM || HAVE_NS */
+
#ifdef HAVE_XPM
#ifdef HAVE_NTGUI
/* Indicate to xpm.h that we don't have Xlib. */
@@ -4140,7 +4270,7 @@ enum xpm_keyword_index
XPM_LAST
};
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
/* Vector of image_keyword structures describing the format
of valid XPM image specifications. */
@@ -4158,7 +4288,7 @@ static const struct image_keyword xpm_format[XPM_LAST] =
{":color-symbols", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
{":background", IMAGE_STRING_OR_NIL_VALUE, 0}
};
-#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU */
+#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK */
#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
@@ -4382,7 +4512,7 @@ init_xpm_functions (void)
#endif /* WINDOWSNT */
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
/* Value is true if COLOR_SYMBOLS is a valid color symbols list
for XPM images. Such a list must consist of conses whose car and
cdr are strings. */
@@ -4418,7 +4548,7 @@ xpm_image_p (Lisp_Object object)
&& (! fmt[XPM_COLOR_SYMBOLS].count
|| xpm_valid_color_symbols_p (fmt[XPM_COLOR_SYMBOLS].value)));
}
-#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU */
+#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK */
#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU */
@@ -4790,7 +4920,8 @@ xpm_load (struct frame *f, struct image *img)
#if (defined USE_CAIRO && defined HAVE_XPM) \
|| (defined HAVE_NS && !defined HAVE_XPM) \
- || (defined HAVE_HAIKU && !defined HAVE_XPM)
+ || (defined HAVE_HAIKU && !defined HAVE_XPM) \
+ || (defined HAVE_PGTK && !defined HAVE_XPM)
/* XPM support functions for NS and Haiku where libxpm is not available, and for
Cairo. Only XPM version 3 (without any extensions) is supported. */
@@ -4989,7 +5120,7 @@ xpm_load_image (struct frame *f,
Lisp_Object (*get_color_table) (Lisp_Object, const char *, int);
Lisp_Object frame, color_symbols, color_table;
int best_key;
-#ifndef HAVE_NS
+#if !defined (HAVE_NS)
bool have_mask = false;
#endif
Emacs_Pix_Container ximg = NULL, mask_img = NULL;
@@ -9511,11 +9642,15 @@ imagemagick_load_image (struct frame *f, struct image *img,
PixelWand **pixels, *bg_wand = NULL;
MagickPixelPacket pixel;
Lisp_Object image;
+#ifndef DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE
Lisp_Object value;
+#endif
Lisp_Object crop;
EMACS_INT ino;
int desired_width, desired_height;
+#ifndef DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE
double rotation;
+#endif
char hint_buffer[MaxTextExtent];
char *filename_hint = NULL;
imagemagick_initialize ();
@@ -9632,9 +9767,13 @@ imagemagick_load_image (struct frame *f, struct image *img,
PixelSetBlue (bg_wand, (double) bgcolor.blue / 65535);
}
+#ifndef DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE
compute_image_size (MagickGetImageWidth (image_wand),
MagickGetImageHeight (image_wand),
img, &desired_width, &desired_height);
+#else
+ desired_width = desired_height = -1;
+#endif
if (desired_width != -1 && desired_height != -1)
{
@@ -9678,6 +9817,7 @@ imagemagick_load_image (struct frame *f, struct image *img,
}
}
+#ifndef DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE
/* Furthermore :rotation. we need background color and angle for
rotation. */
/*
@@ -9696,6 +9836,7 @@ imagemagick_load_image (struct frame *f, struct image *img,
goto imagemagick_error;
}
}
+#endif
/* Set the canvas background color to the frame or specified
background, and flatten the image. Note: as of ImageMagick
@@ -9827,8 +9968,8 @@ imagemagick_load_image (struct frame *f, struct image *img,
color_scale * pixel.red,
color_scale * pixel.green,
color_scale * pixel.blue));
- }
- }
+ }
+ }
DestroyPixelIterator (iterator);
}
@@ -11140,7 +11281,7 @@ static struct image_type const image_types[] =
{ SYMBOL_INDEX (Qjpeg), jpeg_image_p, jpeg_load, image_clear_image,
IMAGE_TYPE_INIT (init_jpeg_functions) },
#endif
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
{ SYMBOL_INDEX (Qxpm), xpm_image_p, xpm_load, image_clear_image,
IMAGE_TYPE_INIT (init_xpm_functions) },
#endif
@@ -11288,7 +11429,8 @@ non-numeric, there is no explicit limit on the size of images. */);
DEFSYM (Qxbm, "xbm");
add_image_type (Qxbm);
-#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_HAIKU)
+#if defined (HAVE_XPM) || defined (HAVE_NS) \
+ || defined (HAVE_HAIKU) || defined (HAVE_PGTK)
DEFSYM (Qxpm, "xpm");
add_image_type (Qxpm);
#endif
diff --git a/src/keyboard.c b/src/keyboard.c
index ee4b18b0128..821a1b576be 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -3972,6 +3972,9 @@ kbd_buffer_get_event (KBOARD **kbp,
*used_mouse_menu = true;
FALLTHROUGH;
#endif
+#ifdef HAVE_PGTK
+ case PGTK_PREEDIT_TEXT_EVENT:
+#endif
#ifdef HAVE_NTGUI
case END_SESSION_EVENT:
case LANGUAGE_CHANGE_EVENT:
@@ -6212,6 +6215,12 @@ make_lispy_event (struct input_event *event)
case CONFIG_CHANGED_EVENT:
return list3 (Qconfig_changed_event,
event->arg, event->frame_or_window);
+
+#ifdef HAVE_PGTK
+ case PGTK_PREEDIT_TEXT_EVENT:
+ return list2 (intern ("pgtk-preedit-text"), event->arg);
+#endif
+
/* The 'kind' field of the event is something we don't recognize. */
default:
emacs_abort ();
@@ -12685,6 +12694,8 @@ keys_of_keyboard (void)
"ns-put-working-text");
initial_define_lispy_key (Vspecial_event_map, "ns-unput-working-text",
"ns-unput-working-text");
+ initial_define_lispy_key (Vspecial_event_map, "pgtk-preedit-text",
+ "pgtk-preedit-text");
/* Here we used to use `ignore-event' which would simple set prefix-arg to
current-prefix-arg, as is done in `handle-switch-frame'.
But `handle-switch-frame is not run from the special-map.
diff --git a/src/menu.c b/src/menu.c
index 96d1c5208a9..b9da85ef3d5 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -424,7 +424,7 @@ single_menu_item (Lisp_Object key, Lisp_Object item, Lisp_Object dummy, void *sk
AREF (item_properties, ITEM_PROPERTY_HELP));
#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) \
- || defined (HAVE_NTGUI) || defined (HAVE_HAIKU)
+ || defined (HAVE_NTGUI) || defined (HAVE_HAIKU) || defined (HAVE_PGTK)
/* Display a submenu using the toolkit. */
if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame))
&& ! (NILP (map) || NILP (enabled)))
diff --git a/src/menu.h b/src/menu.h
index 6c67ab20bb0..30b946c0ea4 100644
--- a/src/menu.h
+++ b/src/menu.h
@@ -59,6 +59,12 @@ extern Lisp_Object ns_menu_show (struct frame *, int, int, int,
Lisp_Object, const char **);
extern void ns_activate_menubar (struct frame *);
#endif
+#ifdef HAVE_PGTK
+extern Lisp_Object pgtk_menu_show (struct frame *, int, int, int,
+ Lisp_Object, const char **);
+extern void pgtk_activate_menubar (struct frame *);
+#endif
+
extern Lisp_Object tty_menu_show (struct frame *, int, int, int,
Lisp_Object, const char **);
extern ptrdiff_t menu_item_width (const unsigned char *);
diff --git a/src/pgtkfns.c b/src/pgtkfns.c
new file mode 100644
index 00000000000..5fc5bb4bf22
--- /dev/null
+++ b/src/pgtkfns.c
@@ -0,0 +1,4159 @@
+/* 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"
+#include "atimer.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;
+}
+
+/* On Wayland,
+ * even if without WAYLAND_DISPLAY, --display DISPLAY works, but
+ * gdk_display_get_name() always return "wayland-0", which may be
+ * different from DISPLAY.
+ * If with WAYLAND_DISPLAY, then it always returns WAYLAND_DISPLAY.
+ * So pgtk emacs is confused and enter multi display environment.
+ * To workaround this situation, treat all the wayland-* as the same
+ * display.
+ */
+static Lisp_Object
+is_wayland_display (Lisp_Object dpyname)
+{
+ const char *p = SSDATA (dpyname);
+ if (strncmp (p, "wayland-", 8) != 0)
+ return Qnil;
+ p += 8;
+ do {
+ if (*p < '0' || *p > '9')
+ return Qnil;
+ } while (*++p != '\0');
+ return Qt;
+}
+
+/* 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);
+
+ if (!NILP (is_wayland_display (name)))
+ {
+ for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
+ if (!NILP (is_wayland_display (XCAR (dpyinfo->name_list_element))))
+ return dpyinfo;
+ }
+ else
+ {
+ 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);
+
+ FRAME_X_OUTPUT (f)->background_color = bg;
+
+ xg_set_background_color (f, bg);
+ update_face_from_frame_parameter (f, Qbackground_color, arg);
+
+ 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)
+{
+ /* 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_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_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)
+{
+ /* 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;
+ 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, Qtab_bar_lines, make_fixnum (lines));
+
+ 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);
+
+ 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.");
+}
+
+
+/***********************************************************************
+ 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);
+}
+
+static void
+update_watched_scale_factor (struct atimer *timer)
+{
+ struct frame *f = timer->client_data;
+ double scale_factor = FRAME_SCALE_FACTOR (f);
+
+ if (scale_factor != FRAME_X_OUTPUT (f)->watched_scale_factor)
+ {
+ FRAME_X_OUTPUT (f)->watched_scale_factor = scale_factor;
+ pgtk_cr_update_surface_desired_size (f,
+ FRAME_CR_SURFACE_DESIRED_WIDTH (f),
+ FRAME_CR_SURFACE_DESIRED_HEIGHT (f),
+ true);
+ }
+}
+
+/* ==========================================================================
+
+ 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;
+
+ 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 =
+ 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 ();
+
+ 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))
+ {
+ GList *w = gtk_container_get_children (GTK_CONTAINER (FRAME_GTK_OUTER_WIDGET (f)));
+ for (; w != NULL; w = w->next)
+ gtk_widget_show_all (GTK_WIDGET (w->data));
+ }
+
+ 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;
+
+ /* 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;
+ FRAME_X_OUTPUT (f)->watched_scale_factor = 1.0;
+ struct timespec ts = make_timespec (1, 0);
+ FRAME_X_OUTPUT (f)->scale_factor_atimer = start_atimer(ATIMER_CONTINUOUS,
+ ts,
+ update_watched_scale_factor,
+ f);
+
+ /* 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;
+}
+
+#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;
+}
+
+double
+pgtk_frame_scale_factor (struct frame *f)
+{
+ struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ GdkDisplay *gdpy = dpyinfo->gdpy;
+
+ block_input ();
+
+ GdkWindow *gwin = gtk_widget_get_window (FRAME_GTK_WIDGET (f));
+ GdkMonitor *gmon = gdk_display_get_monitor_at_window (gdpy, gwin);
+
+ /* GTK returns scaled sizes for the workareas. */
+ double scale = pgtk_get_monitor_scale_factor (gdk_monitor_get_model (gmon));
+ if (scale == 0.0)
+ scale = gdk_monitor_get_scale_factor (gmon);
+
+ unblock_input ();
+
+ return scale;
+}
+
+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;
+ ptrdiff_t count = SPECPDL_INDEX ();
+ bool face_change_before = face_change;
+
+ 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);
+
+ 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 = true;
+ xg_show_tooltip (f, string);
+ 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, false);
+
+ 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
+ {
+ GtkAllocation alloc;
+
+ if (FRAME_GTK_WIDGET (f) == NULL)
+ return Qnil; /* This can occur while creating a frame. */
+
+ 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);
+
+ 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);
+}
+
+DEFUN ("x-select-font", Fx_select_font, Sx_select_font, 0, 2, 0,
+ doc: /* Read a font using a GTK dialog and return a font spec.
+
+FRAME is the frame on which to pop up the font chooser. If omitted or
+nil, it defaults to the selected frame. */)
+ (Lisp_Object frame, Lisp_Object ignored)
+{
+ struct frame *f = decode_window_system_frame (frame);
+ Lisp_Object font;
+ Lisp_Object font_param;
+ char *default_name = NULL;
+ ptrdiff_t count = SPECPDL_INDEX ();
+
+ if (popup_activated ())
+ error ("Trying to use a menu from within a menu-entry");
+ else
+ pgtk_menu_set_in_use (true);
+
+ /* Prevent redisplay. */
+ specbind (Qinhibit_redisplay, Qt);
+ record_unwind_protect_void (clean_up_dialog);
+
+ 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);
+}
+
+/* ==========================================================================
+
+ 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_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);
+ defsubr (&Sx_select_font);
+
+ 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");
+}
+
+#endif
diff --git a/src/pgtkgui.h b/src/pgtkgui.h
new file mode 100644
index 00000000000..7342dacc5b7
--- /dev/null
+++ b/src/pgtkgui.h
@@ -0,0 +1,119 @@
+/* Definitions and headers for communication on the pure Gtk+3.
+ Copyright (C) 1995, 2005, 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/>. */
+
+#ifndef __PGTKGUI_H__
+#define __PGTKGUI_H__
+
+/* Emulate XCharStruct. */
+typedef struct _XCharStruct
+{
+ int rbearing;
+ int lbearing;
+ int width;
+ int ascent;
+ int descent;
+} XCharStruct;
+
+/* Fake structure from Xlib.h to represent two-byte characters. */
+typedef unsigned short unichar;
+typedef unichar XChar2b;
+
+#define STORE_XCHAR2B(chp, b1, b2) \
+ (*(chp) = ((XChar2b)((((b1) & 0x00ff) << 8) | ((b2) & 0x00ff))))
+
+#define XCHAR2B_BYTE1(chp) \
+ ((*(chp) & 0xff00) >> 8)
+
+#define XCHAR2B_BYTE2(chp) \
+ (*(chp) & 0x00ff)
+
+
+typedef struct _GdkCursor *Emacs_Cursor;
+
+typedef void *Color;
+typedef int Window;
+typedef struct _GdkDisplay Display;
+
+/* Xism */
+typedef void *XrmDatabase;
+
+
+/* some sort of attempt to normalize rectangle handling.. seems a bit much
+ for what is accomplished */
+typedef struct
+{
+ int x, y;
+ unsigned width, height;
+} XRectangle;
+
+/* This stuff needed by frame.c. */
+#define ForgetGravity 0
+#define NorthWestGravity 1
+#define NorthGravity 2
+#define NorthEastGravity 3
+#define WestGravity 4
+#define CenterGravity 5
+#define EastGravity 6
+#define SouthWestGravity 7
+#define SouthGravity 8
+#define SouthEastGravity 9
+#define StaticGravity 10
+
+#define NoValue 0x0000
+#define XValue 0x0001
+#define YValue 0x0002
+#define WidthValue 0x0004
+#define HeightValue 0x0008
+#define AllValues 0x000F
+#define XNegative 0x0010
+#define YNegative 0x0020
+
+#define USPosition (1L << 0) /* user specified x, y */
+#define USSize (1L << 1) /* user specified width, height */
+
+#define PPosition (1L << 2) /* program specified position */
+#define PSize (1L << 3) /* program specified size */
+#define PMinSize (1L << 4) /* program specified minimum size */
+#define PMaxSize (1L << 5) /* program specified maximum size */
+#define PResizeInc (1L << 6) /* program specified resize increments */
+#define PAspect (1L << 7) /* program specified min, max aspect ratios */
+#define PBaseSize (1L << 8) /* program specified base for incrementing */
+#define PWinGravity (1L << 9) /* program specified window gravity */
+
+
+#define NativeRectangle XRectangle
+
+#define CONVERT_TO_EMACS_RECT(xr, nr) \
+ ((xr).x = (nr).x, \
+ (xr).y = (nr).y, \
+ (xr).width = (nr).width, \
+ (xr).height = (nr).height)
+
+#define CONVERT_FROM_EMACS_RECT(xr, nr) \
+ ((nr).x = (xr).x, \
+ (nr).y = (xr).y, \
+ (nr).width = (xr).width, \
+ (nr).height = (xr).height)
+
+#define STORE_NATIVE_RECT(nr, px, py, pwidth, pheight) \
+ ((nr).x = (px), \
+ (nr).y = (py), \
+ (nr).width = (pwidth), \
+ (nr).height = (pheight))
+
+#endif /* __PGTKGUI_H__ */
diff --git a/src/pgtkim.c b/src/pgtkim.c
new file mode 100644
index 00000000000..ba69a27501a
--- /dev/null
+++ b/src/pgtkim.c
@@ -0,0 +1,311 @@
+/* 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 "pgtkterm.h"
+
+static void
+im_context_commit_cb (GtkIMContext * imc, gchar * str, gpointer user_data)
+{
+ struct pgtk_display_info *dpyinfo = user_data;
+ struct frame *f = dpyinfo->im.focused_frame;
+
+ if (dpyinfo->im.context == NULL)
+ return;
+ if (f == NULL)
+ return;
+
+ pgtk_enqueue_string (f, str);
+}
+
+static gboolean
+im_context_retrieve_surrounding_cb (GtkIMContext * imc, gpointer user_data)
+{
+ gtk_im_context_set_surrounding (imc, "", -1, 0);
+ return TRUE;
+}
+
+static gboolean
+im_context_delete_surrounding_cb (GtkIMContext * imc, int offset, int n_chars,
+ gpointer user_data)
+{
+ return TRUE;
+}
+
+static Lisp_Object
+make_color_string (PangoAttrColor * pac)
+{
+ char buf[256];
+ sprintf (buf, "#%02x%02x%02x",
+ pac->color.red >> 8, pac->color.green >> 8, pac->color.blue >> 8);
+ return build_string (buf);
+}
+
+static void
+im_context_preedit_changed_cb (GtkIMContext * imc, gpointer user_data)
+{
+ struct pgtk_display_info *dpyinfo = user_data;
+ struct frame *f = dpyinfo->im.focused_frame;
+ char *str;
+ PangoAttrList *attrs;
+ int pos;
+
+ if (dpyinfo->im.context == NULL)
+ return;
+ if (f == NULL)
+ return;
+
+ gtk_im_context_get_preedit_string (imc, &str, &attrs, &pos);
+
+
+ /*
+ * (
+ * (TEXT (ul . COLOR) (bg . COLOR) (fg . COLOR))
+ * ...
+ * )
+ */
+ Lisp_Object list = Qnil;
+
+ PangoAttrIterator *iter;
+ iter = pango_attr_list_get_iterator (attrs);
+ do
+ {
+ int st, ed;
+ int has_underline = 0;
+ Lisp_Object part = Qnil;
+
+ pango_attr_iterator_range (iter, &st, &ed);
+
+ if (ed > strlen (str))
+ ed = strlen (str);
+ if (st >= ed)
+ continue;
+
+ Lisp_Object text = make_string (str + st, ed - st);
+ part = Fcons (text, part);
+
+ PangoAttrInt *ul =
+ (PangoAttrInt *) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
+ if (ul != NULL)
+ {
+ if (ul->value != PANGO_UNDERLINE_NONE)
+ has_underline = 1;
+ }
+
+ PangoAttrColor *pac;
+ if (has_underline)
+ {
+ pac =
+ (PangoAttrColor *) pango_attr_iterator_get (iter,
+ PANGO_ATTR_UNDERLINE_COLOR);
+ if (pac != NULL)
+ part = Fcons (Fcons (Qul, make_color_string (pac)), part);
+ else
+ part = Fcons (Fcons (Qul, Qt), part);
+ }
+
+ pac =
+ (PangoAttrColor *) pango_attr_iterator_get (iter,
+ PANGO_ATTR_FOREGROUND);
+ if (pac != NULL)
+ part = Fcons (Fcons (Qfg, make_color_string (pac)), part);
+
+ pac =
+ (PangoAttrColor *) pango_attr_iterator_get (iter,
+ PANGO_ATTR_BACKGROUND);
+ if (pac != NULL)
+ part = Fcons (Fcons (Qbg, make_color_string (pac)), part);
+
+ part = Fnreverse (part);
+ list = Fcons (part, list);
+ }
+ while (pango_attr_iterator_next (iter));
+
+ list = Fnreverse (list);
+ pgtk_enqueue_preedit (f, list);
+
+ g_free (str);
+ pango_attr_list_unref (attrs);
+}
+
+static void
+im_context_preedit_end_cb (GtkIMContext * imc, gpointer user_data)
+{
+ struct pgtk_display_info *dpyinfo = user_data;
+ struct frame *f = dpyinfo->im.focused_frame;
+
+ if (dpyinfo->im.context == NULL)
+ return;
+ if (f == NULL)
+ return;
+
+ pgtk_enqueue_preedit (f, Qnil);
+}
+
+static void
+im_context_preedit_start_cb (GtkIMContext * imc, gpointer user_data)
+{
+}
+
+void
+pgtk_im_focus_in (struct frame *f)
+{
+ struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ if (dpyinfo->im.context != NULL)
+ {
+ gtk_im_context_reset (dpyinfo->im.context);
+ gtk_im_context_set_client_window (dpyinfo->im.context,
+ gtk_widget_get_window
+ (FRAME_GTK_WIDGET (f)));
+ gtk_im_context_focus_in (dpyinfo->im.context);
+ }
+ dpyinfo->im.focused_frame = f;
+}
+
+void
+pgtk_im_focus_out (struct frame *f)
+{
+ struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ if (dpyinfo->im.focused_frame == f)
+ {
+ if (dpyinfo->im.context != NULL)
+ {
+ gtk_im_context_reset (dpyinfo->im.context);
+ gtk_im_context_focus_out (dpyinfo->im.context);
+ gtk_im_context_set_client_window (dpyinfo->im.context, NULL);
+ }
+ dpyinfo->im.focused_frame = NULL;
+ }
+}
+
+bool
+pgtk_im_filter_keypress (struct frame *f, GdkEventKey * ev)
+{
+ struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ if (dpyinfo->im.context != NULL)
+ {
+ if (gtk_im_context_filter_keypress (dpyinfo->im.context, ev))
+ return true;
+ }
+ return false;
+}
+
+void
+pgtk_im_set_cursor_location (struct frame *f, int x, int y, int width,
+ int height)
+{
+ struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ if (dpyinfo->im.context != NULL && dpyinfo->im.focused_frame == f)
+ {
+ GdkRectangle area = { x, y, width, height };
+ gtk_im_context_set_cursor_location (dpyinfo->im.context, &area);
+ }
+}
+
+static void
+pgtk_im_use_context (struct pgtk_display_info *dpyinfo, bool use_p)
+{
+ if (!use_p)
+ {
+ if (dpyinfo->im.context != NULL)
+ {
+ gtk_im_context_reset (dpyinfo->im.context);
+ gtk_im_context_focus_out (dpyinfo->im.context);
+ gtk_im_context_set_client_window (dpyinfo->im.context, NULL);
+
+ g_object_unref (dpyinfo->im.context);
+ dpyinfo->im.context = NULL;
+ }
+ }
+ else
+ {
+ if (dpyinfo->im.context == NULL)
+ {
+ dpyinfo->im.context = gtk_im_multicontext_new ();
+ g_signal_connect (dpyinfo->im.context, "commit",
+ G_CALLBACK (im_context_commit_cb), dpyinfo);
+ g_signal_connect (dpyinfo->im.context, "retrieve-surrounding",
+ G_CALLBACK (im_context_retrieve_surrounding_cb),
+ dpyinfo);
+ g_signal_connect (dpyinfo->im.context, "delete-surrounding",
+ G_CALLBACK (im_context_delete_surrounding_cb),
+ dpyinfo);
+ g_signal_connect (dpyinfo->im.context, "preedit-changed",
+ G_CALLBACK (im_context_preedit_changed_cb),
+ dpyinfo);
+ g_signal_connect (dpyinfo->im.context, "preedit-end",
+ G_CALLBACK (im_context_preedit_end_cb), dpyinfo);
+ g_signal_connect (dpyinfo->im.context, "preedit-start",
+ G_CALLBACK (im_context_preedit_start_cb),
+ dpyinfo);
+ gtk_im_context_set_use_preedit (dpyinfo->im.context, TRUE);
+
+ if (dpyinfo->im.focused_frame)
+ pgtk_im_focus_in (dpyinfo->im.focused_frame);
+ }
+ }
+}
+
+void
+pgtk_im_init (struct pgtk_display_info *dpyinfo)
+{
+ dpyinfo->im.context = NULL;
+
+ pgtk_im_use_context (dpyinfo, !NILP (Vpgtk_use_im_context_on_new_connection));
+}
+
+void
+pgtk_im_finish (struct pgtk_display_info *dpyinfo)
+{
+ if (dpyinfo->im.context != NULL)
+ g_object_unref (dpyinfo->im.context);
+ dpyinfo->im.context = NULL;
+}
+
+DEFUN ("pgtk-use-im-context", Fpgtk_use_im_context, Spgtk_use_im_context, 1, 2, 0,
+ doc: /* Set whether to use GtkIMContext. */)
+ (Lisp_Object use_p, Lisp_Object terminal)
+{
+ struct pgtk_display_info *dpyinfo = check_pgtk_display_info (terminal);
+
+ pgtk_im_use_context (dpyinfo, !NILP (use_p));
+
+ return Qnil;
+}
+
+void
+syms_of_pgtkim (void)
+{
+ defsubr (&Spgtk_use_im_context);
+
+ DEFSYM (Qpgtk_refresh_preedit, "pgtk-refresh-preedit");
+ DEFSYM (Qul, "ul");
+ DEFSYM (Qfg, "fg");
+ DEFSYM (Qbg, "bg");
+
+ DEFVAR_LISP ("pgtk-use-im-context-on-new-connection", Vpgtk_use_im_context_on_new_connection,
+ doc: /* Whether to use GtkIMContext on a new connection.
+If you want to change it after connection, use the `pgtk-use-im-context'
+function. */ );
+ Vpgtk_use_im_context_on_new_connection = Qt;
+}
diff --git a/src/pgtkmenu.c b/src/pgtkmenu.c
new file mode 100644
index 00000000000..fd2c53a1b82
--- /dev/null
+++ b/src/pgtkmenu.c
@@ -0,0 +1,1159 @@
+/* Pure GTK3 menu and toolbar module.
+ Copyright (C) 2019-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 "lisp.h"
+#include "frame.h"
+#include "window.h"
+#include "character.h"
+#include "buffer.h"
+#include "keymap.h"
+#include "coding.h"
+#include "commands.h"
+#include "blockinput.h"
+#include "termhooks.h"
+#include "keyboard.h"
+#include "menu.h"
+#include "pdumper.h"
+#include "xgselect.h"
+
+#include "gtkutil.h"
+#include <gtk/gtk.h>
+
+/* Flag which when set indicates a dialog or menu has been posted by
+ Xt on behalf of one of the widget sets. */
+static int popup_activated_flag;
+
+/* Set menu_items_inuse so no other popup menu or dialog is created. */
+
+void
+pgtk_menu_set_in_use (bool in_use)
+{
+ Lisp_Object frames, frame;
+
+ menu_items_inuse = in_use;
+ popup_activated_flag = in_use;
+
+ /* Don't let frames in `above' z-group obscure popups. */
+ FOR_EACH_FRAME (frames, frame)
+ {
+ struct frame *f = XFRAME (frame);
+
+ if (in_use && FRAME_Z_GROUP_ABOVE (f))
+ x_set_z_group (f, Qabove_suspended, Qabove);
+ else if (!in_use && FRAME_Z_GROUP_ABOVE_SUSPENDED (f))
+ x_set_z_group (f, Qabove, Qabove_suspended);
+ }
+}
+
+DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
+ doc: /* Start key navigation of the menu bar in FRAME.
+ This initially opens the first menu bar item and you can then navigate with the
+ arrow keys, select a menu entry with the return key or cancel with the
+ escape key. If FRAME has no menu bar this function does nothing.
+
+ If FRAME is nil or not given, use the selected frame. */)
+ (Lisp_Object frame)
+{
+ GtkWidget *menubar;
+ struct frame *f;
+
+ block_input ();
+ f = decode_window_system_frame (frame);
+
+ if (FRAME_EXTERNAL_MENU_BAR (f))
+ set_frame_menubar (f, true);
+
+ menubar = FRAME_X_OUTPUT (f)->menubar_widget;
+ if (menubar)
+ {
+ /* Activate the first menu. */
+ GList *children = gtk_container_get_children (GTK_CONTAINER (menubar));
+
+ if (children)
+ {
+ g_signal_emit_by_name (children->data, "activate_item");
+ g_list_free (children);
+ }
+ }
+ unblock_input ();
+
+ return Qnil;
+}
+
+/* Loop util popup_activated_flag is set to zero in a callback.
+ Used for popup menus and dialogs. */
+
+static void
+popup_widget_loop (bool do_timers, GtkWidget *widget)
+{
+ ++popup_activated_flag;
+
+ /* Process events in the Gtk event loop until done. */
+ while (popup_activated_flag)
+ gtk_main_iteration ();
+}
+
+void
+pgtk_activate_menubar (struct frame *f)
+{
+ set_frame_menubar (f, true);
+
+ popup_activated_flag = 1;
+
+ /* f->output_data.pgtk->menubar_active = 1; */
+}
+
+/* This callback is invoked when a dialog or menu is finished being
+ used and has been unposted. */
+
+static void
+popup_deactivate_callback (GtkWidget *widget, gpointer client_data)
+{
+ popup_activated_flag = 0;
+}
+
+/* Function that finds the frame for WIDGET and shows the HELP text
+ for that widget.
+ F is the frame if known, or NULL if not known. */
+static void
+show_help_event (struct frame *f, GtkWidget *widget, Lisp_Object help)
+{
+ /* Don't show this tooltip.
+ * Tooltips are always tied to main widget, so stacking order
+ * on Wayland is:
+ * (above)
+ * - menu
+ * - tooltip
+ * - main widget
+ * (below)
+ * This is applicable to tooltips for menu, and menu tooltips
+ * are shown below menus.
+ * As a workaround, I entrust Gtk with menu tooltips, and
+ * let emacs not to show menu tooltips.
+ */
+
+#if 0
+ Lisp_Object frame;
+
+ if (f)
+ {
+ XSETFRAME (frame, f);
+ kbd_buffer_store_help_event (frame, help);
+ }
+ else
+ show_help_echo (help, Qnil, Qnil, Qnil);
+#endif
+}
+
+/* Callback called when menu items are highlighted/unhighlighted
+ while moving the mouse over them. WIDGET is the menu bar or menu
+ popup widget. ID is its LWLIB_ID. CALL_DATA contains a pointer to
+ the data structure for the menu item, or null in case of
+ unhighlighting. */
+
+static void
+menu_highlight_callback (GtkWidget *widget, gpointer call_data)
+{
+ xg_menu_item_cb_data *cb_data;
+ Lisp_Object help;
+
+ cb_data = g_object_get_data (G_OBJECT (widget), XG_ITEM_DATA);
+ if (!cb_data)
+ return;
+
+ help = call_data ? cb_data->help : Qnil;
+
+ /* If popup_activated_flag is greater than 1 we are in a popup menu.
+ Don't pass the frame to show_help_event for those.
+ Passing frame creates an Emacs event. As we are looping in
+ popup_widget_loop, it won't be handled. Passing NULL shows the tip
+ directly without using an Emacs event. This is what the Lucid code
+ does below. */
+ show_help_event (popup_activated_flag <= 1 ? cb_data->cl_data->f : NULL,
+ widget, help);
+}
+
+/* Gtk calls callbacks just because we tell it what item should be
+ selected in a radio group. If this variable is set to a non-zero
+ value, we are creating menus and don't want callbacks right now.
+*/
+static bool xg_crazy_callback_abort;
+
+/* This callback is called from the menu bar pulldown menu
+ when the user makes a selection.
+ Figure out what the user chose
+ and put the appropriate events into the keyboard buffer. */
+static void
+menubar_selection_callback (GtkWidget *widget, gpointer client_data)
+{
+ xg_menu_item_cb_data *cb_data = client_data;
+
+ if (xg_crazy_callback_abort)
+ return;
+
+ if (!cb_data || !cb_data->cl_data || !cb_data->cl_data->f)
+ return;
+
+ /* For a group of radio buttons, GTK calls the selection callback first
+ for the item that was active before the selection and then for the one that
+ is active after the selection. For C-h k this means we get the help on
+ the deselected item and then the selected item is executed. Prevent that
+ by ignoring the non-active item. */
+ if (GTK_IS_RADIO_MENU_ITEM (widget)
+ && !gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)))
+ return;
+
+ /* When a menu is popped down, X generates a focus event (i.e. focus
+ goes back to the frame below the menu). Since GTK buffers events,
+ we force it out here before the menu selection event. Otherwise
+ sit-for will exit at once if the focus event follows the menu selection
+ event. */
+
+ block_input ();
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ unblock_input ();
+
+ find_and_call_menu_selection (cb_data->cl_data->f,
+ cb_data->cl_data->menu_bar_items_used,
+ cb_data->cl_data->menu_bar_vector,
+ cb_data->call_data);
+}
+
+/* Recompute all the widgets of frame F, when the menu bar has been
+ changed. */
+
+static void
+update_frame_menubar (struct frame *f)
+{
+ xg_update_frame_menubar (f);
+}
+
+/* Set the contents of the menubar widgets of frame F.
+ The argument FIRST_TIME is currently ignored;
+ it is set the first time this is called, from initialize_frame_menubar. */
+
+void
+set_frame_menubar (struct frame *f, bool deep_p)
+{
+ GtkWidget *menubar_widget;
+ Lisp_Object items;
+ widget_value *wv, *first_wv, *prev_wv = 0;
+ int i;
+ int *submenu_start, *submenu_end;
+ bool *submenu_top_level_items;
+ int *submenu_n_panes;
+
+
+ menubar_widget = f->output_data.pgtk->menubar_widget;
+
+ XSETFRAME (Vmenu_updating_frame, f);
+
+ if (!menubar_widget)
+ deep_p = true;
+
+ if (deep_p)
+ {
+ struct buffer *prev = current_buffer;
+ Lisp_Object buffer;
+ ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+ int previous_menu_items_used = f->menu_bar_items_used;
+ Lisp_Object *previous_items
+ = alloca (previous_menu_items_used * sizeof *previous_items);
+ int subitems;
+
+ /* If we are making a new widget, its contents are empty,
+ do always reinitialize them. */
+ if (!menubar_widget)
+ previous_menu_items_used = 0;
+
+ buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
+ specbind (Qinhibit_quit, Qt);
+ /* Don't let the debugger step into this code
+ because it is not reentrant. */
+ specbind (Qdebug_on_next_call, Qnil);
+
+ record_unwind_save_match_data ();
+ if (NILP (Voverriding_local_map_menu_flag))
+ {
+ specbind (Qoverriding_terminal_local_map, Qnil);
+ specbind (Qoverriding_local_map, Qnil);
+ }
+
+ set_buffer_internal_1 (XBUFFER (buffer));
+
+ /* Run the Lucid hook. */
+ safe_run_hooks (Qactivate_menubar_hook);
+
+ /* If it has changed current-menubar from previous value,
+ really recompute the menubar from the value. */
+ if (!NILP (Vlucid_menu_bar_dirty_flag))
+ call0 (Qrecompute_lucid_menubar);
+ safe_run_hooks (Qmenu_bar_update_hook);
+ fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
+
+ items = FRAME_MENU_BAR_ITEMS (f);
+
+ /* Save the frame's previous menu bar contents data. */
+ if (previous_menu_items_used)
+ memcpy (previous_items, xvector_contents (f->menu_bar_vector),
+ previous_menu_items_used * word_size);
+
+ /* Fill in menu_items with the current menu bar contents.
+ This can evaluate Lisp code. */
+ save_menu_items ();
+
+ menu_items = f->menu_bar_vector;
+ menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
+ subitems = ASIZE (items) / 4;
+ submenu_start = alloca ((subitems + 1) * sizeof *submenu_start);
+ submenu_end = alloca (subitems * sizeof *submenu_end);
+ submenu_n_panes = alloca (subitems * sizeof *submenu_n_panes);
+ submenu_top_level_items = alloca (subitems
+ * sizeof *submenu_top_level_items);
+ init_menu_items ();
+ for (i = 0; i < subitems; i++)
+ {
+ Lisp_Object key, string, maps;
+
+ key = AREF (items, 4 * i);
+ string = AREF (items, 4 * i + 1);
+ maps = AREF (items, 4 * i + 2);
+ if (NILP (string))
+ break;
+
+ submenu_start[i] = menu_items_used;
+
+ menu_items_n_panes = 0;
+ submenu_top_level_items[i]
+ = parse_single_submenu (key, string, maps);
+ submenu_n_panes[i] = menu_items_n_panes;
+
+ submenu_end[i] = menu_items_used;
+ }
+
+ submenu_start[i] = -1;
+ finish_menu_items ();
+
+ /* Convert menu_items into widget_value trees
+ to display the menu. This cannot evaluate Lisp code. */
+
+ wv = make_widget_value ("menubar", NULL, true, Qnil);
+ wv->button_type = BUTTON_TYPE_NONE;
+ first_wv = wv;
+
+ for (i = 0; submenu_start[i] >= 0; i++)
+ {
+ menu_items_n_panes = submenu_n_panes[i];
+ wv = digest_single_submenu (submenu_start[i], submenu_end[i],
+ submenu_top_level_items[i]);
+ if (prev_wv)
+ prev_wv->next = wv;
+ else
+ first_wv->contents = wv;
+ /* Don't set wv->name here; GC during the loop might relocate it. */
+ wv->enabled = true;
+ wv->button_type = BUTTON_TYPE_NONE;
+ prev_wv = wv;
+ }
+
+ set_buffer_internal_1 (prev);
+
+ /* If there has been no change in the Lisp-level contents
+ of the menu bar, skip redisplaying it. Just exit. */
+
+ /* Compare the new menu items with the ones computed last time. */
+ for (i = 0; i < previous_menu_items_used; i++)
+ if (menu_items_used == i
+ || (!EQ (previous_items[i], AREF (menu_items, i))))
+ break;
+ if (i == menu_items_used && i == previous_menu_items_used && i != 0)
+ {
+ /* The menu items have not changed. Don't bother updating
+ the menus in any form, since it would be a no-op. */
+ free_menubar_widget_value_tree (first_wv);
+ discard_menu_items ();
+ unbind_to (specpdl_count, Qnil);
+ return;
+ }
+
+ /* The menu items are different, so store them in the frame. */
+ fset_menu_bar_vector (f, menu_items);
+ f->menu_bar_items_used = menu_items_used;
+
+ /* This undoes save_menu_items. */
+ unbind_to (specpdl_count, Qnil);
+
+ /* Now GC cannot happen during the lifetime of the widget_value,
+ so it's safe to store data from a Lisp_String. */
+ wv = first_wv->contents;
+ for (i = 0; i < ASIZE (items); i += 4)
+ {
+ Lisp_Object string;
+ string = AREF (items, i + 1);
+ if (NILP (string))
+ break;
+ wv->name = SSDATA (string);
+ update_submenu_strings (wv->contents);
+ wv = wv->next;
+ }
+
+ }
+ else
+ {
+ /* Make a widget-value tree containing
+ just the top level menu bar strings. */
+
+ wv = make_widget_value ("menubar", NULL, true, Qnil);
+ wv->button_type = BUTTON_TYPE_NONE;
+ first_wv = wv;
+
+ items = FRAME_MENU_BAR_ITEMS (f);
+ for (i = 0; i < ASIZE (items); i += 4)
+ {
+ Lisp_Object string;
+
+ string = AREF (items, i + 1);
+ if (NILP (string))
+ break;
+
+ wv = make_widget_value (SSDATA (string), NULL, true, Qnil);
+ wv->button_type = BUTTON_TYPE_NONE;
+ /* This prevents lwlib from assuming this
+ menu item is really supposed to be empty. */
+ /* The intptr_t cast avoids a warning.
+ This value just has to be different from small integers. */
+ wv->call_data = (void *) (intptr_t) (-1);
+
+ if (prev_wv)
+ prev_wv->next = wv;
+ else
+ first_wv->contents = wv;
+ prev_wv = wv;
+ }
+
+ /* Forget what we thought we knew about what is in the
+ detailed contents of the menu bar menus.
+ Changing the top level always destroys the contents. */
+ f->menu_bar_items_used = 0;
+ }
+
+ block_input ();
+
+ xg_crazy_callback_abort = true;
+ if (menubar_widget)
+ {
+ /* The fourth arg is DEEP_P, which says to consider the entire
+ menu trees we supply, rather than just the menu bar item names. */
+ xg_modify_menubar_widgets (menubar_widget,
+ f,
+ first_wv,
+ deep_p,
+ G_CALLBACK (menubar_selection_callback),
+ G_CALLBACK (popup_deactivate_callback),
+ G_CALLBACK (menu_highlight_callback));
+ }
+ else
+ {
+ menubar_widget
+ = xg_create_widget ("menubar", "menubar", f, first_wv,
+ G_CALLBACK (menubar_selection_callback),
+ G_CALLBACK (popup_deactivate_callback),
+ G_CALLBACK (menu_highlight_callback));
+
+ f->output_data.pgtk->menubar_widget = menubar_widget;
+ }
+
+ free_menubar_widget_value_tree (first_wv);
+ update_frame_menubar (f);
+
+ xg_crazy_callback_abort = false;
+
+ unblock_input ();
+}
+
+/* Called from Fx_create_frame to create the initial menubar of a frame
+ before it is mapped, so that the window is mapped with the menubar already
+ there instead of us tacking it on later and thrashing the window after it
+ is visible. */
+
+void
+initialize_frame_menubar (struct frame *f)
+{
+ /* This function is called before the first chance to redisplay
+ the frame. It has to be, so the frame will have the right size. */
+ fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
+ set_frame_menubar (f, true);
+}
+
+
+/* x_menu_show actually displays a menu using the panes and items in menu_items
+ and returns the value selected from it.
+ There are two versions of x_menu_show, one for Xt and one for Xlib.
+ Both assume input is blocked by the caller. */
+
+/* F is the frame the menu is for.
+ X and Y are the frame-relative specified position,
+ relative to the inside upper left corner of the frame F.
+ Bitfield MENUFLAGS bits are:
+ MENU_FOR_CLICK is set if this menu was invoked for a mouse click.
+ MENU_KEYMAPS is set if this menu was specified with keymaps;
+ in that case, we return a list containing the chosen item's value
+ and perhaps also the pane's prefix.
+ TITLE is the specified menu title.
+ ERROR is a place to store an error message string in case of failure.
+ (We return nil on failure, but the value doesn't actually matter.) */
+
+/* The item selected in the popup menu. */
+static Lisp_Object *volatile menu_item_selection;
+
+static void
+popup_selection_callback (GtkWidget *widget, gpointer client_data)
+{
+ xg_menu_item_cb_data *cb_data = client_data;
+
+ if (xg_crazy_callback_abort)
+ return;
+ if (cb_data)
+ menu_item_selection = cb_data->call_data;
+}
+
+static void
+pop_down_menu (void *arg)
+{
+ popup_activated_flag = 0;
+ block_input ();
+ gtk_widget_destroy (GTK_WIDGET (arg));
+ unblock_input ();
+}
+
+/* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the
+ menu pops down.
+ menu_item_selection will be set to the selection. */
+static void
+create_and_show_popup_menu (struct frame *f, widget_value * first_wv,
+ int x, int y, bool for_click)
+{
+ GtkWidget *menu;
+ ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+
+ eassert (FRAME_PGTK_P (f));
+
+ xg_crazy_callback_abort = true;
+ menu = xg_create_widget ("popup", first_wv->name, f, first_wv,
+ G_CALLBACK (popup_selection_callback),
+ G_CALLBACK (popup_deactivate_callback),
+ G_CALLBACK (menu_highlight_callback));
+ xg_crazy_callback_abort = false;
+
+ /* Display the menu. */
+ gtk_widget_show_all (menu);
+
+ if (for_click)
+ gtk_menu_popup_at_pointer (GTK_MENU (menu),
+ FRAME_DISPLAY_INFO (f)->last_click_event);
+ else
+ {
+ GdkRectangle rect;
+ rect.x = x;
+ rect.y = y;
+ rect.width = 1;
+ rect.height = 1;
+ gtk_menu_popup_at_rect (GTK_MENU (menu),
+ gtk_widget_get_window (FRAME_GTK_WIDGET (f)),
+ &rect,
+ GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_NORTH_WEST,
+ FRAME_DISPLAY_INFO (f)->last_click_event);
+ }
+
+ record_unwind_protect_ptr (pop_down_menu, menu);
+
+ if (gtk_widget_get_mapped (menu))
+ {
+ /* Set this to one. popup_widget_loop increases it by one, so it becomes
+ two. show_help_echo uses this to detect popup menus. */
+ popup_activated_flag = 1;
+ /* Process events that apply to the menu. */
+ popup_widget_loop (true, menu);
+ }
+
+ unbind_to (specpdl_count, Qnil);
+
+ /* Must reset this manually because the button release event is not passed
+ to Emacs event loop. */
+ FRAME_DISPLAY_INFO (f)->grabbed = 0;
+}
+
+static void
+cleanup_widget_value_tree (void *arg)
+{
+ free_menubar_widget_value_tree (arg);
+}
+
+Lisp_Object
+pgtk_menu_show (struct frame *f, int x, int y, int menuflags,
+ Lisp_Object title, const char **error_name)
+{
+ int i;
+ widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
+ widget_value **submenu_stack
+ = alloca (menu_items_used * sizeof *submenu_stack);
+ Lisp_Object *subprefix_stack
+ = alloca (menu_items_used * sizeof *subprefix_stack);
+ int submenu_depth = 0;
+
+ ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+
+ eassert (FRAME_PGTK_P (f));
+
+ *error_name = NULL;
+
+ if (!FRAME_GTK_OUTER_WIDGET (f)) {
+ *error_name = "Can't popup from child frames.";
+ return Qnil;
+ }
+
+ if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
+ {
+ *error_name = "Empty menu";
+ return Qnil;
+ }
+
+ block_input ();
+
+ /* Create a tree of widget_value objects
+ representing the panes and their items. */
+ wv = make_widget_value ("menu", NULL, true, Qnil);
+ wv->button_type = BUTTON_TYPE_NONE;
+ first_wv = wv;
+ bool first_pane = true;
+
+ /* Loop over all panes and items, filling in the tree. */
+ i = 0;
+ while (i < menu_items_used)
+ {
+ if (NILP (AREF (menu_items, i)))
+ {
+ submenu_stack[submenu_depth++] = save_wv;
+ save_wv = prev_wv;
+ prev_wv = 0;
+ first_pane = true;
+ i++;
+ }
+ else if (EQ (AREF (menu_items, i), Qlambda))
+ {
+ prev_wv = save_wv;
+ save_wv = submenu_stack[--submenu_depth];
+ first_pane = false;
+ i++;
+ }
+ else if (EQ (AREF (menu_items, i), Qt) && submenu_depth != 0)
+ i += MENU_ITEMS_PANE_LENGTH;
+ /* Ignore a nil in the item list.
+ It's meaningful only for dialog boxes. */
+ else if (EQ (AREF (menu_items, i), Qquote))
+ i += 1;
+ else if (EQ (AREF (menu_items, i), Qt))
+ {
+ /* Create a new pane. */
+ Lisp_Object pane_name, prefix;
+ const char *pane_string;
+
+ pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
+ prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+
+#ifndef HAVE_MULTILINGUAL_MENU
+ if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
+ {
+ pane_name = ENCODE_MENU_STRING (pane_name);
+ ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
+ }
+#endif
+ pane_string = (NILP (pane_name) ? "" : SSDATA (pane_name));
+ /* If there is just one top-level pane, put all its items directly
+ under the top-level menu. */
+ if (menu_items_n_panes == 1)
+ pane_string = "";
+
+ /* If the pane has a meaningful name,
+ make the pane a top-level menu item
+ with its items as a submenu beneath it. */
+ if (!(menuflags & MENU_KEYMAPS) && strcmp (pane_string, ""))
+ {
+ wv = make_widget_value (pane_string, NULL, true, Qnil);
+ if (save_wv)
+ save_wv->next = wv;
+ else
+ first_wv->contents = wv;
+ if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
+ wv->name++;
+ wv->button_type = BUTTON_TYPE_NONE;
+ save_wv = wv;
+ prev_wv = 0;
+ }
+ else if (first_pane)
+ {
+ save_wv = wv;
+ prev_wv = 0;
+ }
+ first_pane = false;
+ i += MENU_ITEMS_PANE_LENGTH;
+ }
+ else
+ {
+ /* Create a new item within current pane. */
+ Lisp_Object item_name, enable, descrip, def, type, selected, help;
+ item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+ enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+ descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
+ def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
+ type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
+ selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
+ help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
+
+#ifndef HAVE_MULTILINGUAL_MENU
+ if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
+ {
+ item_name = ENCODE_MENU_STRING (item_name);
+ ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
+ }
+
+ if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
+ {
+ descrip = ENCODE_MENU_STRING (descrip);
+ ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
+ }
+#endif /* not HAVE_MULTILINGUAL_MENU */
+
+ wv = make_widget_value (SSDATA (item_name), NULL, !NILP (enable),
+ STRINGP (help) ? help : Qnil);
+ if (prev_wv)
+ prev_wv->next = wv;
+ else
+ save_wv->contents = wv;
+ if (!NILP (descrip))
+ wv->key = SSDATA (descrip);
+ /* If this item has a null value,
+ make the call_data null so that it won't display a box
+ when the mouse is on it. */
+ wv->call_data = !NILP (def) ? aref_addr (menu_items, i) : 0;
+
+ if (NILP (type))
+ wv->button_type = BUTTON_TYPE_NONE;
+ else if (EQ (type, QCtoggle))
+ wv->button_type = BUTTON_TYPE_TOGGLE;
+ else if (EQ (type, QCradio))
+ wv->button_type = BUTTON_TYPE_RADIO;
+ else
+ emacs_abort ();
+
+ wv->selected = !NILP (selected);
+
+ prev_wv = wv;
+
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ /* Deal with the title, if it is non-nil. */
+ if (!NILP (title))
+ {
+ widget_value *wv_title;
+ widget_value *wv_sep1 = make_widget_value ("--", NULL, false, Qnil);
+ widget_value *wv_sep2 = make_widget_value ("--", NULL, false, Qnil);
+
+ wv_sep2->next = first_wv->contents;
+ wv_sep1->next = wv_sep2;
+
+#ifndef HAVE_MULTILINGUAL_MENU
+ if (STRING_MULTIBYTE (title))
+ title = ENCODE_MENU_STRING (title);
+#endif
+
+ wv_title = make_widget_value (SSDATA (title), NULL, true, Qnil);
+ wv_title->button_type = BUTTON_TYPE_NONE;
+ wv_title->next = wv_sep1;
+ first_wv->contents = wv_title;
+ }
+
+ /* No selection has been chosen yet. */
+ menu_item_selection = 0;
+
+ /* Make sure to free the widget_value objects we used to specify the
+ contents even with longjmp. */
+ record_unwind_protect_ptr (cleanup_widget_value_tree, first_wv);
+
+ /* Actually create and show the menu until popped down. */
+ create_and_show_popup_menu (f, first_wv, x, y, menuflags & MENU_FOR_CLICK);
+
+ unbind_to (specpdl_count, Qnil);
+
+ /* Find the selected item, and its pane, to return
+ the proper value. */
+ if (menu_item_selection != 0)
+ {
+ Lisp_Object prefix, entry;
+
+ prefix = entry = Qnil;
+ i = 0;
+ while (i < menu_items_used)
+ {
+ if (NILP (AREF (menu_items, i)))
+ {
+ subprefix_stack[submenu_depth++] = prefix;
+ prefix = entry;
+ i++;
+ }
+ else if (EQ (AREF (menu_items, i), Qlambda))
+ {
+ prefix = subprefix_stack[--submenu_depth];
+ i++;
+ }
+ else if (EQ (AREF (menu_items, i), Qt))
+ {
+ prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+ i += MENU_ITEMS_PANE_LENGTH;
+ }
+ /* Ignore a nil in the item list.
+ It's meaningful only for dialog boxes. */
+ else if (EQ (AREF (menu_items, i), Qquote))
+ i += 1;
+ else
+ {
+ entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+ if (menu_item_selection == aref_addr (menu_items, i))
+ {
+ if (menuflags & MENU_KEYMAPS)
+ {
+ int j;
+
+ entry = list1 (entry);
+ if (!NILP (prefix))
+ entry = Fcons (prefix, entry);
+ for (j = submenu_depth - 1; j >= 0; j--)
+ if (!NILP (subprefix_stack[j]))
+ entry = Fcons (subprefix_stack[j], entry);
+ }
+ unblock_input ();
+ return entry;
+ }
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+ }
+ else if (!(menuflags & MENU_FOR_CLICK))
+ {
+ unblock_input ();
+ /* Make "Cancel" equivalent to C-g. */
+ quit ();
+ }
+
+ unblock_input ();
+ return Qnil;
+}
+
+static void
+dialog_selection_callback (GtkWidget *widget, gpointer client_data)
+{
+ /* Treat the pointer as an integer. There's no problem
+ as long as pointers have enough bits to hold small integers. */
+ if ((intptr_t) client_data != -1)
+ menu_item_selection = client_data;
+
+ popup_activated_flag = 0;
+}
+
+/* Pop up the dialog for frame F defined by FIRST_WV and loop until the
+ dialog pops down.
+ menu_item_selection will be set to the selection. */
+static void
+create_and_show_dialog (struct frame *f, widget_value *first_wv)
+{
+ GtkWidget *menu;
+
+ eassert (FRAME_PGTK_P (f));
+
+ menu = xg_create_widget ("dialog", first_wv->name, f, first_wv,
+ G_CALLBACK (dialog_selection_callback),
+ G_CALLBACK (popup_deactivate_callback), 0);
+
+ if (menu)
+ {
+ ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+ record_unwind_protect_ptr (pop_down_menu, menu);
+
+ /* Display the menu. */
+ gtk_widget_show_all (menu);
+
+ /* Process events that apply to the menu. */
+ popup_widget_loop (true, menu);
+
+ unbind_to (specpdl_count, Qnil);
+ }
+}
+
+static const char *button_names[] = {
+ "button1", "button2", "button3", "button4", "button5",
+ "button6", "button7", "button8", "button9", "button10"
+};
+
+Lisp_Object
+pgtk_dialog_show (struct frame *f, Lisp_Object title,
+ Lisp_Object header, const char **error_name)
+{
+ int i, nb_buttons = 0;
+ char dialog_name[6];
+
+ widget_value *wv, *first_wv = 0, *prev_wv = 0;
+
+ /* Number of elements seen so far, before boundary. */
+ int left_count = 0;
+ /* Whether we've seen the boundary between left-hand elts and right-hand. */
+ bool boundary_seen = false;
+
+ ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+
+ eassert (FRAME_PGTK_P (f));
+
+ *error_name = NULL;
+
+ if (!FRAME_GTK_OUTER_WIDGET (f)) {
+ *error_name = "Can't popup from child frames.";
+ return Qnil;
+ }
+
+ if (menu_items_n_panes > 1)
+ {
+ *error_name = "Multiple panes in dialog box";
+ return Qnil;
+ }
+
+ /* Create a tree of widget_value objects
+ representing the text label and buttons. */
+ {
+ Lisp_Object pane_name;
+ const char *pane_string;
+ pane_name = AREF (menu_items, MENU_ITEMS_PANE_NAME);
+ pane_string = (NILP (pane_name) ? "" : SSDATA (pane_name));
+ prev_wv = make_widget_value ("message", (char *) pane_string, true, Qnil);
+ first_wv = prev_wv;
+
+ /* Loop over all panes and items, filling in the tree. */
+ i = MENU_ITEMS_PANE_LENGTH;
+ while (i < menu_items_used)
+ {
+
+ /* Create a new item within current pane. */
+ Lisp_Object item_name, enable, descrip;
+ item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+ enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+ descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
+
+ if (NILP (item_name))
+ {
+ free_menubar_widget_value_tree (first_wv);
+ *error_name = "Submenu in dialog items";
+ return Qnil;
+ }
+ if (EQ (item_name, Qquote))
+ {
+ /* This is the boundary between left-side elts
+ and right-side elts. Stop incrementing right_count. */
+ boundary_seen = true;
+ i++;
+ continue;
+ }
+ if (nb_buttons >= 9)
+ {
+ free_menubar_widget_value_tree (first_wv);
+ *error_name = "Too many dialog items";
+ return Qnil;
+ }
+
+ wv = make_widget_value (button_names[nb_buttons],
+ SSDATA (item_name), !NILP (enable), Qnil);
+ prev_wv->next = wv;
+ if (!NILP (descrip))
+ wv->key = SSDATA (descrip);
+ wv->call_data = aref_addr (menu_items, i);
+ prev_wv = wv;
+
+ if (!boundary_seen)
+ left_count++;
+
+ nb_buttons++;
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+
+ /* If the boundary was not specified,
+ by default put half on the left and half on the right. */
+ if (!boundary_seen)
+ left_count = nb_buttons - nb_buttons / 2;
+
+ wv = make_widget_value (dialog_name, NULL, false, Qnil);
+
+ /* Frame title: 'Q' = Question, 'I' = Information.
+ Can also have 'E' = Error if, one day, we want
+ a popup for errors. */
+ if (NILP (header))
+ dialog_name[0] = 'Q';
+ else
+ dialog_name[0] = 'I';
+
+ /* Dialog boxes use a really stupid name encoding
+ which specifies how many buttons to use
+ and how many buttons are on the right. */
+ dialog_name[1] = '0' + nb_buttons;
+ dialog_name[2] = 'B';
+ dialog_name[3] = 'R';
+ /* Number of buttons to put on the right. */
+ dialog_name[4] = '0' + nb_buttons - left_count;
+ dialog_name[5] = 0;
+ wv->contents = first_wv;
+ first_wv = wv;
+ }
+
+ /* No selection has been chosen yet. */
+ menu_item_selection = 0;
+
+ /* Make sure to free the widget_value objects we used to specify the
+ contents even with longjmp. */
+ record_unwind_protect_ptr (cleanup_widget_value_tree, first_wv);
+
+ /* Actually create and show the dialog. */
+ create_and_show_dialog (f, first_wv);
+
+ unbind_to (specpdl_count, Qnil);
+
+ /* Find the selected item, and its pane, to return
+ the proper value. */
+ if (menu_item_selection != 0)
+ {
+ i = 0;
+ while (i < menu_items_used)
+ {
+ Lisp_Object entry;
+
+ if (EQ (AREF (menu_items, i), Qt))
+ i += MENU_ITEMS_PANE_LENGTH;
+ else if (EQ (AREF (menu_items, i), Qquote))
+ {
+ /* This is the boundary between left-side elts and
+ right-side elts. */
+ ++i;
+ }
+ else
+ {
+ entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+ if (menu_item_selection == aref_addr (menu_items, i))
+ return entry;
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+ }
+ else
+ /* Make "Cancel" equivalent to C-g. */
+ quit ();
+
+ return Qnil;
+}
+
+Lisp_Object
+pgtk_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
+{
+ Lisp_Object title;
+ const char *error_name;
+ Lisp_Object selection;
+ ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+
+ check_window_system (f);
+
+ /* Decode the dialog items from what was specified. */
+ title = Fcar (contents);
+ CHECK_STRING (title);
+ record_unwind_protect_void (unuse_menu_items);
+
+ if (NILP (Fcar (Fcdr (contents))))
+ /* No buttons specified, add an "Ok" button so users can pop down
+ the dialog. Also, the lesstif/motif version crashes if there are
+ no buttons. */
+ contents = list2 (title, Fcons (build_string ("Ok"), Qt));
+
+ list_of_panes (list1 (contents));
+
+ /* Display them in a dialog box. */
+ block_input ();
+ selection = pgtk_dialog_show (f, title, header, &error_name);
+ unblock_input ();
+
+ unbind_to (specpdl_count, Qnil);
+ discard_menu_items ();
+
+ if (error_name)
+ error ("%s", error_name);
+ return selection;
+}
+
+/* Detect if a dialog or menu has been posted. MSDOS has its own
+ implementation on msdos.c. */
+
+int
+popup_activated (void)
+{
+ return popup_activated_flag;
+}
+
+/* The following is used by delayed window autoselection. */
+
+DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
+ doc: /* Return t if a menu or popup dialog is active.
+\(On MS Windows, this refers to the selected frame.) */)
+ (void)
+{
+ return (popup_activated ())? Qt : Qnil;
+}
+
+static void syms_of_pgtkmenu_for_pdumper (void);
+
+void
+syms_of_pgtkmenu (void)
+{
+ DEFSYM (Qdebug_on_next_call, "debug-on-next-call");
+ defsubr (&Smenu_or_popup_active_p);
+
+ DEFSYM (Qframe_monitor_workarea, "frame-monitor-workarea");
+
+ defsubr (&Sx_menu_bar_open_internal);
+ Ffset (intern_c_string ("accelerate-menu"),
+ intern_c_string (Sx_menu_bar_open_internal.s.symbol_name));
+
+ pdumper_do_now_and_after_load (syms_of_pgtkmenu_for_pdumper);
+}
+
+static void
+syms_of_pgtkmenu_for_pdumper (void)
+{
+}
diff --git a/src/pgtkselect.c b/src/pgtkselect.c
new file mode 100644
index 00000000000..77a563dc3f3
--- /dev/null
+++ b/src/pgtkselect.c
@@ -0,0 +1,632 @@
+/* Gtk selection processing for emacs.
+ Copyright (C) 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/>. */
+
+/*
+Originally by Carl Edman
+Updated by Christian Limpach (chris@nice.ch)
+OpenStep/Rhapsody port by Scott Bender (sbender@harmony-ds.com)
+macOS/Aqua port by Christophe de Dinechin (descubes@earthlink.net)
+GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
+*/
+
+/* This should be the first include, as it may set up #defines affecting
+ interpretation of even the system includes. */
+#include <config.h>
+
+#include "lisp.h"
+#include "pgtkterm.h"
+#include "termhooks.h"
+#include "keyboard.h"
+#include "pgtkselect.h"
+#include <gdk/gdk.h>
+
+#if 0
+static Lisp_Object Vselection_alist;
+#endif
+
+static GQuark quark_primary_data = 0;
+static GQuark quark_primary_size = 0;
+static GQuark quark_secondary_data = 0;
+static GQuark quark_secondary_size = 0;
+static GQuark quark_clipboard_data = 0;
+static GQuark quark_clipboard_size = 0;
+
+/* ==========================================================================
+
+ Internal utility functions
+
+ ========================================================================== */
+
+/* From a Lisp_Object, return a suitable frame for selection
+ operations. OBJECT may be a frame, a terminal object, or nil
+ (which stands for the selected frame--or, if that is not an pgtk
+ frame, the first pgtk display on the list). If no suitable frame can
+ be found, return NULL. */
+
+static struct frame *
+frame_for_pgtk_selection (Lisp_Object object)
+{
+ Lisp_Object tail, frame;
+ struct frame *f;
+
+ if (NILP (object))
+ {
+ f = XFRAME (selected_frame);
+ if (FRAME_PGTK_P (f) && FRAME_LIVE_P (f))
+ return f;
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ f = XFRAME (frame);
+ if (FRAME_PGTK_P (f) && FRAME_LIVE_P (f))
+ return f;
+ }
+ }
+ else if (TERMINALP (object))
+ {
+ struct terminal *t = decode_live_terminal (object);
+
+ if (t->type == output_pgtk)
+ FOR_EACH_FRAME (tail, frame)
+ {
+ f = XFRAME (frame);
+ if (FRAME_LIVE_P (f) && f->terminal == t)
+ return f;
+ }
+ }
+ else if (FRAMEP (object))
+ {
+ f = XFRAME (object);
+ if (FRAME_PGTK_P (f) && FRAME_LIVE_P (f))
+ return f;
+ }
+
+ return NULL;
+}
+
+static GtkClipboard *
+symbol_to_gtk_clipboard (GtkWidget * widget, Lisp_Object symbol)
+{
+ GdkAtom atom;
+
+ CHECK_SYMBOL (symbol);
+ if (NILP (symbol))
+ {
+ atom = GDK_SELECTION_PRIMARY;
+ }
+ else if (EQ (symbol, QCLIPBOARD))
+ {
+ atom = GDK_SELECTION_CLIPBOARD;
+ }
+ else if (EQ (symbol, QPRIMARY))
+ {
+ atom = GDK_SELECTION_PRIMARY;
+ }
+ else if (EQ (symbol, QSECONDARY))
+ {
+ atom = GDK_SELECTION_SECONDARY;
+ }
+ else if (EQ (symbol, Qt))
+ {
+ atom = GDK_SELECTION_SECONDARY;
+ }
+ else
+ {
+ atom = 0;
+ error ("Bad selection");
+ }
+
+ return gtk_widget_get_clipboard (widget, atom);
+}
+
+static void
+selection_type_to_quarks (GdkAtom type, GQuark * quark_data,
+ GQuark * quark_size)
+{
+ if (type == GDK_SELECTION_PRIMARY)
+ {
+ *quark_data = quark_primary_data;
+ *quark_size = quark_primary_size;
+ }
+ else if (type == GDK_SELECTION_SECONDARY)
+ {
+ *quark_data = quark_secondary_data;
+ *quark_size = quark_secondary_size;
+ }
+ else if (type == GDK_SELECTION_CLIPBOARD)
+ {
+ *quark_data = quark_clipboard_data;
+ *quark_size = quark_clipboard_size;
+ }
+ else
+ {
+ /* fixme: Is it safe to use 'error' here? */
+ error ("Unknown selection type.");
+ }
+}
+
+static void
+get_func (GtkClipboard * cb, GtkSelectionData * data, guint info,
+ gpointer user_data_or_owner)
+{
+ GObject *obj = G_OBJECT (user_data_or_owner);
+ const char *str;
+ int size;
+ GQuark quark_data, quark_size;
+
+ selection_type_to_quarks (gtk_clipboard_get_selection (cb), &quark_data,
+ &quark_size);
+
+ str = g_object_get_qdata (obj, quark_data);
+ size = GPOINTER_TO_SIZE (g_object_get_qdata (obj, quark_size));
+ gtk_selection_data_set_text (data, str, size);
+}
+
+static void
+clear_func (GtkClipboard * cb, gpointer user_data_or_owner)
+{
+ GObject *obj = G_OBJECT (user_data_or_owner);
+ GQuark quark_data, quark_size;
+
+ selection_type_to_quarks (gtk_clipboard_get_selection (cb), &quark_data,
+ &quark_size);
+
+ g_object_set_qdata (obj, quark_data, NULL);
+ g_object_set_qdata (obj, quark_size, 0);
+}
+
+
+/* ==========================================================================
+
+ Functions used externally
+
+ ========================================================================== */
+
+void
+pgtk_selection_init (void)
+{
+ if (quark_primary_data == 0)
+ {
+ quark_primary_data = g_quark_from_static_string ("pgtk-primary-data");
+ quark_primary_size = g_quark_from_static_string ("pgtk-primary-size");
+ quark_secondary_data =
+ g_quark_from_static_string ("pgtk-secondary-data");
+ quark_secondary_size =
+ g_quark_from_static_string ("pgtk-secondary-size");
+ quark_clipboard_data =
+ g_quark_from_static_string ("pgtk-clipboard-data");
+ quark_clipboard_size =
+ g_quark_from_static_string ("pgtk-clipboard-size");
+ }
+}
+
+void
+pgtk_selection_lost (GtkWidget * widget, GdkEventSelection * event,
+ gpointer user_data)
+{
+ GQuark quark_data, quark_size;
+
+ selection_type_to_quarks (event->selection, &quark_data, &quark_size);
+
+ g_object_set_qdata (G_OBJECT (widget), quark_data, NULL);
+ g_object_set_qdata (G_OBJECT (widget), quark_size, 0);
+}
+
+static bool
+pgtk_selection_usable (void)
+{
+ if (pgtk_enable_selection_on_multi_display)
+ return true;
+
+ /*
+ * https://github.com/GNOME/gtk/blob/gtk-3-24/gdk/wayland/gdkselection-wayland.c#L1033
+ *
+ * Gdk uses gdk_display_get_default() when handling selections, so
+ * selections don't work properly on multi-display environment.
+ *
+ * ----------------
+ * #include <gtk/gtk.h>
+ *
+ * static GtkWidget *top1, *top2;
+ *
+ * int main (int argc, char **argv)
+ * {
+ * GtkWidget *w;
+ * GtkTextBuffer *buf;
+ *
+ * gtk_init (&argc, &argv);
+ *
+ * static char *text = "\
+ * It is fine today.\n\
+ * It will be fine tomorrow too.\n\
+ * It is too hot.";
+ *
+ * top1 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ * gtk_window_set_title (GTK_WINDOW (top1), "default");
+ * gtk_widget_show (top1);
+ * w = gtk_text_view_new ();
+ * gtk_container_add (GTK_CONTAINER (top1), w);
+ * gtk_widget_show (w);
+ * buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (w));
+ * gtk_text_buffer_insert_at_cursor (buf, text, strlen (text));
+ * gtk_text_buffer_add_selection_clipboard (buf, gtk_widget_get_clipboard (w, GDK_SELECTION_PRIMARY));
+ *
+ * unsetenv ("GDK_BACKEND");
+ * GdkDisplay *gdpy;
+ * const char *dpyname2;
+ * if (strcmp (G_OBJECT_TYPE_NAME (gtk_widget_get_window (top1)), "GdkWaylandWindow") == 0)
+ * dpyname2 = ":0";
+ * else
+ * dpyname2 = "wayland-0";
+ * gdpy = gdk_display_open (dpyname2);
+ * top2 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ * gtk_window_set_title (GTK_WINDOW (top2), dpyname2);
+ * gtk_window_set_screen (GTK_WINDOW (top2), gdk_display_get_default_screen (gdpy));
+ * gtk_widget_show (top2);
+ * w = gtk_text_view_new ();
+ * gtk_container_add (GTK_CONTAINER (top2), w);
+ * gtk_widget_show (w);
+ * buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (w));
+ * gtk_text_buffer_insert_at_cursor (buf, text, strlen (text));
+ * gtk_text_buffer_add_selection_clipboard (buf, gtk_widget_get_clipboard (w, GDK_SELECTION_PRIMARY));
+ *
+ * gtk_main ();
+ *
+ * return 0;
+ * }
+ * ----------------
+ *
+ * This code fails if
+ * GDK_BACKEND=x11 ./test
+ * and select on both of windows.
+ *
+ * ----------------
+ * (test:15345): GLib-GObject-CRITICAL **: 01:56:38.041: g_object_ref: assertion 'G_IS_OBJECT (object)' failed
+ *
+ * (test:15345): GLib-GObject-CRITICAL **: 01:56:38.042: g_object_ref: assertion 'G_IS_OBJECT (object)' failed
+ *
+ * (test:15345): GLib-GObject-CRITICAL **: 01:56:39.113: g_object_ref: assertion 'G_IS_OBJECT (object)' failed
+ *
+ * (test:15345): GLib-GObject-CRITICAL **: 01:56:39.113: g_object_ref: assertion 'G_IS_OBJECT (object)' failed
+ * ----------------
+ * (gtk-3.24.10)
+ *
+ * This function checks whether selections work by the number of displays.
+ * If you use more than 2 displays, then selection is disabled.
+ */
+
+ GdkDisplayManager *dpyman = gdk_display_manager_get ();
+ GSList *list = gdk_display_manager_list_displays (dpyman);
+ int len = g_slist_length (list);
+ g_slist_free (list);
+ return len < 2;
+}
+
+/* ==========================================================================
+
+ Lisp Defuns
+
+ ========================================================================== */
+
+
+DEFUN ("pgtk-own-selection-internal", Fpgtk_own_selection_internal, Spgtk_own_selection_internal, 2, 3, 0,
+ doc: /* Assert an X selection of type SELECTION and value VALUE.
+SELECTION is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
+\(Those are literal upper-case symbol names, since that's what X expects.)
+VALUE is typically a string, or a cons of two markers, but may be
+anything that the functions on `selection-converter-alist' know about.
+
+FRAME should be a frame that should own the selection. If omitted or
+nil, it defaults to the selected frame. */)
+ (Lisp_Object selection, Lisp_Object value, Lisp_Object frame)
+{
+ Lisp_Object successful_p = Qnil;
+ Lisp_Object target_symbol, rest;
+ GtkClipboard *cb;
+ struct frame *f;
+ GQuark quark_data, quark_size;
+
+ check_window_system (NULL);
+
+ if (!pgtk_selection_usable ())
+ return Qnil;
+
+ if (NILP (frame))
+ frame = selected_frame;
+ if (!FRAME_LIVE_P (XFRAME (frame)) || !FRAME_PGTK_P (XFRAME (frame)))
+ error ("pgtk selection unavailable for this frame");
+ f = XFRAME (frame);
+
+ cb = symbol_to_gtk_clipboard (FRAME_GTK_WIDGET (f), selection);
+ selection_type_to_quarks (gtk_clipboard_get_selection (cb), &quark_data,
+ &quark_size);
+
+ /* We only support copy of text. */
+ target_symbol = QTEXT;
+ if (STRINGP (value))
+ {
+ GtkTargetList *list;
+ GtkTargetEntry *targets;
+ gint n_targets;
+ GtkWidget *widget;
+
+ list = gtk_target_list_new (NULL, 0);
+ gtk_target_list_add_text_targets (list, 0);
+
+ {
+ /* text/plain: Strings encoded by Gtk are not correctly decoded by Chromium(Wayland). */
+ GdkAtom atom_text_plain = gdk_atom_intern ("text/plain", false);
+ gtk_target_list_remove (list, atom_text_plain);
+ }
+
+ targets = gtk_target_table_new_from_list (list, &n_targets);
+
+ int size = SBYTES (value);
+ gchar *str = xmalloc (size + 1);
+ memcpy (str, SSDATA (value), size);
+ str[size] = '\0';
+
+ widget = FRAME_GTK_WIDGET (f);
+ g_object_set_qdata_full (G_OBJECT (widget), quark_data, str, xfree);
+ g_object_set_qdata_full (G_OBJECT (widget), quark_size,
+ GSIZE_TO_POINTER (size), NULL);
+
+ if (gtk_clipboard_set_with_owner (cb,
+ targets, n_targets,
+ get_func, clear_func,
+ G_OBJECT (FRAME_GTK_WIDGET (f))))
+ {
+ successful_p = Qt;
+ }
+ gtk_clipboard_set_can_store (cb, NULL, 0);
+
+ gtk_target_table_free (targets, n_targets);
+ gtk_target_list_unref (list);
+ }
+
+ if (!EQ (Vpgtk_sent_selection_hooks, Qunbound))
+ {
+ /* FIXME: Use run-hook-with-args! */
+ for (rest = Vpgtk_sent_selection_hooks; CONSP (rest);
+ rest = Fcdr (rest))
+ call3 (Fcar (rest), selection, target_symbol, successful_p);
+ }
+
+ return value;
+}
+
+
+DEFUN ("pgtk-disown-selection-internal", Fpgtk_disown_selection_internal, Spgtk_disown_selection_internal, 1, 3, 0,
+ doc: /* If we own the selection SELECTION, disown it.
+Disowning it means there is no such selection.
+
+Sets the last-change time for the selection to TIME-OBJECT (by default
+the time of the last event).
+
+TERMINAL should be a terminal object or a frame specifying the X
+server to query. If omitted or nil, that stands for the selected
+frame's display, or the first available X display.
+
+On Nextstep, the TIME-OBJECT and TERMINAL arguments are unused.
+On MS-DOS, all this does is return non-nil if we own the selection.
+On PGTK, the TIME-OBJECT is unused. */)
+ (Lisp_Object selection, Lisp_Object time_object, Lisp_Object terminal)
+{
+ struct frame *f = frame_for_pgtk_selection (terminal);
+ GtkClipboard *cb;
+
+ if (!pgtk_selection_usable ())
+ return Qnil;
+
+ if (!f)
+ return Qnil;
+
+ cb = symbol_to_gtk_clipboard (FRAME_GTK_WIDGET (f), selection);
+
+ gtk_clipboard_clear (cb);
+
+ return Qt;
+}
+
+
+DEFUN ("pgtk-selection-exists-p", Fpgtk_selection_exists_p, Spgtk_selection_exists_p, 0, 2, 0,
+ doc: /* Whether there is an owner for the given X selection.
+SELECTION should be the name of the selection in question, typically
+one of the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'. (X expects
+these literal upper-case names.) The symbol nil is the same as
+`PRIMARY', and t is the same as `SECONDARY'.
+
+TERMINAL should be a terminal object or a frame specifying the X
+server to query. If omitted or nil, that stands for the selected
+frame's display, or the first available X display.
+
+On Nextstep, TERMINAL is unused. */)
+ (Lisp_Object selection, Lisp_Object terminal)
+{
+ struct frame *f = frame_for_pgtk_selection (terminal);
+ GtkClipboard *cb;
+
+ if (!pgtk_selection_usable ())
+ return Qnil;
+
+ if (!f)
+ return Qnil;
+
+ cb = symbol_to_gtk_clipboard (FRAME_GTK_WIDGET (f), selection);
+
+ return gtk_clipboard_wait_is_text_available (cb) ? Qt : Qnil;
+}
+
+
+DEFUN ("pgtk-selection-owner-p", Fpgtk_selection_owner_p, Spgtk_selection_owner_p, 0, 2, 0,
+ doc: /* Whether the current Emacs process owns the given X Selection.
+The arg should be the name of the selection in question, typically one of
+the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'.
+\(Those are literal upper-case symbol names, since that's what X expects.)
+For convenience, the symbol nil is the same as `PRIMARY',
+and t is the same as `SECONDARY'.
+
+TERMINAL should be a terminal object or a frame specifying the X
+server to query. If omitted or nil, that stands for the selected
+frame's display, or the first available X display.
+
+On Nextstep, TERMINAL is unused. */)
+ (Lisp_Object selection, Lisp_Object terminal)
+{
+ struct frame *f = frame_for_pgtk_selection (terminal);
+ GtkClipboard *cb;
+ GObject *obj;
+ GQuark quark_data, quark_size;
+
+ if (!pgtk_selection_usable ())
+ return Qnil;
+
+ cb = symbol_to_gtk_clipboard (FRAME_GTK_WIDGET (f), selection);
+ selection_type_to_quarks (gtk_clipboard_get_selection (cb), &quark_data,
+ &quark_size);
+
+ obj = gtk_clipboard_get_owner (cb);
+
+ return obj && g_object_get_qdata (obj, quark_data) != NULL ? Qt : Qnil;
+}
+
+
+DEFUN ("pgtk-get-selection-internal", Fpgtk_get_selection_internal, Spgtk_get_selection_internal, 2, 4, 0,
+ doc: /* Return text selected from some X window.
+SELECTION-SYMBOL is typically `PRIMARY', `SECONDARY', or `CLIPBOARD'.
+\(Those are literal upper-case symbol names, since that's what X expects.)
+TARGET-TYPE is the type of data desired, typically `STRING'.
+
+TIME-STAMP is the time to use in the XConvertSelection call for foreign
+selections. If omitted, defaults to the time for the last event.
+
+TERMINAL should be a terminal object or a frame specifying the X
+server to query. If omitted or nil, that stands for the selected
+frame's display, or the first available X display.
+
+On Nextstep, TIME-STAMP and TERMINAL are unused.
+On PGTK, TIME-STAMP is unused. */)
+ (Lisp_Object selection_symbol, Lisp_Object target_type,
+ Lisp_Object time_stamp, Lisp_Object terminal)
+{
+ struct frame *f = frame_for_pgtk_selection (terminal);
+ GtkClipboard *cb;
+
+ CHECK_SYMBOL (selection_symbol);
+ CHECK_SYMBOL (target_type);
+ if (EQ (target_type, QMULTIPLE))
+ error ("Retrieving MULTIPLE selections is currently unimplemented");
+ if (!f)
+ error ("PGTK selection unavailable for this frame");
+
+ if (!pgtk_selection_usable ())
+ return Qnil;
+
+ cb = symbol_to_gtk_clipboard (FRAME_GTK_WIDGET (f), selection_symbol);
+
+ GdkAtom target_atom = gdk_atom_intern (SSDATA (SYMBOL_NAME (target_type)), false);
+ GtkSelectionData *seldata = gtk_clipboard_wait_for_contents (cb, target_atom);
+
+ if (seldata == NULL)
+ return Qnil;
+
+ const guchar *sd_data = gtk_selection_data_get_data (seldata);
+ int sd_len = gtk_selection_data_get_length (seldata);
+ int sd_format = gtk_selection_data_get_format (seldata);
+ GdkAtom sd_type = gtk_selection_data_get_data_type (seldata);
+
+ if (sd_format == 8)
+ {
+ Lisp_Object str, lispy_type;
+
+ str = make_unibyte_string ((char *) sd_data, sd_len);
+ /* Indicate that this string is from foreign selection by a text
+ property `foreign-selection' so that the caller of
+ x-get-selection-internal (usually x-get-selection) can know
+ that the string must be decode. */
+ if (sd_type == gdk_atom_intern ("COMPOUND_TEXT", false))
+ lispy_type = QCOMPOUND_TEXT;
+ else if (sd_type == gdk_atom_intern ("UTF8_STRING", false))
+ lispy_type = QUTF8_STRING;
+ else if (sd_type == gdk_atom_intern ("text/plain;charset=utf-8", false))
+ lispy_type = Qtext_plain_charset_utf_8;
+ else
+ lispy_type = QSTRING;
+ Fput_text_property (make_fixnum (0), make_fixnum (sd_len),
+ Qforeign_selection, lispy_type, str);
+
+ gtk_selection_data_free (seldata);
+ return str;
+ }
+
+ gtk_selection_data_free (seldata);
+ return Qnil;
+}
+
+
+void
+nxatoms_of_pgtkselect (void)
+{
+}
+
+void
+syms_of_pgtkselect (void)
+{
+ DEFSYM (QCLIPBOARD, "CLIPBOARD");
+ DEFSYM (QSECONDARY, "SECONDARY");
+ DEFSYM (QTEXT, "TEXT");
+ DEFSYM (QFILE_NAME, "FILE_NAME");
+ DEFSYM (QMULTIPLE, "MULTIPLE");
+
+ DEFSYM (Qforeign_selection, "foreign-selection");
+ DEFSYM (QUTF8_STRING, "UTF8_STRING");
+ DEFSYM (QSTRING, "STRING");
+ DEFSYM (QCOMPOUND_TEXT, "COMPOUND_TEXT");
+ DEFSYM (Qtext_plain_charset_utf_8, "text/plain;charset=utf-8");
+
+ defsubr (&Spgtk_disown_selection_internal);
+ defsubr (&Spgtk_get_selection_internal);
+ defsubr (&Spgtk_own_selection_internal);
+ defsubr (&Spgtk_selection_exists_p);
+ defsubr (&Spgtk_selection_owner_p);
+
+#if 0
+ Vselection_alist = Qnil;
+ staticpro (&Vselection_alist);
+#endif
+
+ DEFVAR_LISP ("pgtk-sent-selection-hooks", Vpgtk_sent_selection_hooks,
+ "A list of functions to be called when Emacs answers a selection request.\n\
+The functions are called with four arguments:\n\
+ - the selection name (typically `PRIMARY', `SECONDARY', or `CLIPBOARD');\n\
+ - the selection-type which Emacs was asked to convert the\n\
+ selection into before sending (for example, `STRING' or `LENGTH');\n\
+ - a flag indicating success or failure for responding to the request.\n\
+We might have failed (and declined the request) for any number of reasons,\n\
+including being asked for a selection that we no longer own, or being asked\n\
+to convert into a type that we don't know about or that is inappropriate.\n\
+This hook doesn't let you change the behavior of Emacs's selection replies,\n\
+it merely informs you that they have happened.");
+ Vpgtk_sent_selection_hooks = Qnil;
+
+ DEFVAR_BOOL ("pgtk-enable-selection-on-multi-display", pgtk_enable_selection_on_multi_display,
+ doc: /* Enable selection on multi display environment.
+This may cause crash. */);
+ pgtk_enable_selection_on_multi_display = false;
+}
diff --git a/src/pgtkselect.h b/src/pgtkselect.h
new file mode 100644
index 00000000000..7ad04c217ac
--- /dev/null
+++ b/src/pgtkselect.h
@@ -0,0 +1,33 @@
+/* Definitions and headers for selection of pure Gtk+3.
+ Copyright (C) 1989, 1993, 2005, 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/>. */
+
+
+#include "dispextern.h"
+#include "frame.h"
+
+#ifdef HAVE_PGTK
+
+#include <gtk/gtk.h>
+
+extern void pgtk_selection_init (void);
+extern void pgtk_selection_lost (GtkWidget * widget,
+ GdkEventSelection * event,
+ gpointer user_data);
+
+#endif /* HAVE_PGTK */
diff --git a/src/pgtkterm.c b/src/pgtkterm.c
new file mode 100644
index 00000000000..bd61c65edde
--- /dev/null
+++ b/src/pgtkterm.c
@@ -0,0 +1,7115 @@
+/* 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"
+
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/gdkwayland.h>
+#endif
+
+#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)))
+
+/* 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)
+{
+ 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.
+ -------------------------------------------------------------------------- */
+{
+ 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
+ -------------------------------------------------------------------------- */
+{
+}
+
+/* Raise frame F. */
+
+static void
+pgtk_raise_frame (struct frame *f)
+{
+ /* This works only for non-child frames on X.
+ It does not work for child frames on X, and it does not work
+ on Wayland too. */
+ block_input ();
+ if (FRAME_VISIBLE_P (f))
+ gdk_window_raise (gtk_widget_get_window (FRAME_WIDGET (f)));
+ unblock_input ();
+}
+
+/* Lower frame F. */
+
+static void
+pgtk_lower_frame (struct frame *f)
+{
+ if (FRAME_VISIBLE_P (f))
+ {
+ block_input ();
+ gdk_window_lower (gtk_widget_get_window (FRAME_WIDGET (f)));
+ unblock_input ();
+ }
+}
+
+static void
+pgtk_frame_raise_lower (struct frame *f, bool raise_flag)
+{
+ if (raise_flag)
+ pgtk_raise_frame (f);
+ else
+ pgtk_lower_frame (f);
+}
+
+/* Free X resources of frame F. */
+
+void
+x_free_frame_resources (struct frame *f)
+{
+ struct pgtk_display_info *dpyinfo;
+ Mouse_HLInfo *hlinfo;
+
+ check_window_system (f);
+ dpyinfo = FRAME_DISPLAY_INFO (f);
+ hlinfo = MOUSE_HL_INFO (f);
+
+ block_input ();
+
+ free_frame_faces (f);
+
+ if (FRAME_X_OUTPUT (f)->scale_factor_atimer != NULL)
+ {
+ cancel_atimer (FRAME_X_OUTPUT (f)->scale_factor_atimer);
+ FRAME_X_OUTPUT (f)->scale_factor_atimer = NULL;
+ }
+
+#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);
+
+ 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
+ -------------------------------------------------------------------------- */
+{
+ 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.
+ -------------------------------------------------------------------------- */
+{
+ 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, pixelwidth, pixelheight);
+ gtk_widget_queue_resize (FRAME_WIDGET (f));
+
+ unblock_input ();
+}
+
+void
+pgtk_iconify_frame (struct frame *f)
+/* --------------------------------------------------------------------------
+ External: Iconify window
+ -------------------------------------------------------------------------- */
+{
+ /* 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;
+}
+
+static void
+pgtk_wait_for_map_event (struct frame *f, bool multiple_times)
+{
+ 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);
+
+ if (!multiple_times)
+ {
+ while (!found && !timed_out)
+ gtk_main_iteration ();
+ }
+ else
+ {
+ while (!timed_out)
+ gtk_main_iteration ();
+ }
+
+ g_signal_handler_disconnect (FRAME_WIDGET (f), id);
+ if (!timed_out)
+ g_source_remove (src);
+ }
+}
+
+void
+pgtk_make_frame_visible (struct frame *f)
+/* --------------------------------------------------------------------------
+ External: Show the window (X11 semantics)
+ -------------------------------------------------------------------------- */
+{
+ 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));
+
+ pgtk_wait_for_map_event (f, false);
+ }
+}
+
+
+void
+pgtk_make_frame_invisible (struct frame *f)
+/* --------------------------------------------------------------------------
+ External: Hide the window (X11 semantics)
+ -------------------------------------------------------------------------- */
+{
+ gtk_widget_hide (FRAME_WIDGET (f));
+
+ /* Map events are emitted many times, and
+ * map_event() do SET_FRAME_VISIBLE(f, 1).
+ * I expect visible = 0, so process those map events here and
+ * SET_FRAME_VISIBLE(f, 0) after that.
+ */
+ pgtk_wait_for_map_event (f, true);
+
+ 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)
+{
+ 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. */
+ return font_object;
+ }
+
+ FRAME_FONT (f) = font;
+
+ 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);
+
+ return font_object;
+}
+
+int
+x_display_pixel_height (struct pgtk_display_info *dpyinfo)
+{
+ GdkDisplay *gdpy = dpyinfo->gdpy;
+ GdkScreen *gscr = gdk_display_get_default_screen (gdpy);
+ return gdk_screen_get_height (gscr);
+}
+
+int
+x_display_pixel_width (struct pgtk_display_info *dpyinfo)
+{
+ GdkDisplay *gdpy = dpyinfo->gdpy;
+ GdkScreen *gscr = gdk_display_get_default_screen (gdpy);
+ 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);
+
+ /* Remember the css provider, and restore it later. */
+ GtkCssProvider *provider = FRAME_X_OUTPUT (f)->border_color_css_provider;
+ FRAME_X_OUTPUT (f)->border_color_css_provider = NULL;
+ {
+ GtkStyleContext *ctxt = gtk_widget_get_style_context (FRAME_WIDGET (f));
+ if (provider != NULL)
+ gtk_style_context_remove_provider (ctxt, GTK_STYLE_PROVIDER (provider));
+ }
+
+ {
+ GtkWidget *whbox_of_f = gtk_widget_get_parent (fixed);
+ /* Here, unhighlight can be called and may change border_color_css_provider. */
+ gtk_container_remove (GTK_CONTAINER (whbox_of_f), fixed);
+
+ 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);
+ }
+
+ /* Restore css provider. */
+ GtkStyleContext *ctxt = gtk_widget_get_style_context (FRAME_WIDGET (f));
+ GtkCssProvider *old = FRAME_X_OUTPUT (f)->border_color_css_provider;
+ FRAME_X_OUTPUT (f)->border_color_css_provider = provider;
+ if (provider != NULL)
+ {
+ gtk_style_context_add_provider (ctxt, GTK_STYLE_PROVIDER (provider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+ }
+ if (old != NULL)
+ {
+ gtk_style_context_remove_provider (ctxt, GTK_STYLE_PROVIDER (old));
+ g_object_unref(old);
+ }
+
+ 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. */
+{
+ /* 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. */
+
+ 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. */
+
+ 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)
+{
+ 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)
+ s->xgcv = FRAME_X_OUTPUT (s->f)->cursor_xgcv;
+ else
+ {
+ /* Cursor on non-default face: must merge. */
+ Emacs_GC xgcv;
+
+ xgcv.background = FRAME_X_OUTPUT (s->f)->cursor_color;
+ xgcv.foreground = s->face->background;
+
+ /* If the glyph would be invisible, try a different foreground. */
+ if (xgcv.foreground == xgcv.background)
+ xgcv.foreground = s->face->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;
+
+ /* 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;
+ }
+
+ 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)
+{
+ 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)
+{
+ prepare_face_for_display (s->f, s->face);
+
+ 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;
+ }
+ else if (s->hl == DRAW_INVERSE_VIDEO)
+ {
+ x_set_mode_line_face_gc (s);
+ s->stippled_p = s->face->stipple != 0;
+ }
+ else if (s->hl == DRAW_CURSOR)
+ {
+ x_set_cursor_gc (s);
+ s->stippled_p = false;
+ }
+ else if (s->hl == DRAW_MOUSE_FACE)
+ {
+ x_set_mouse_face_gc (s);
+ s->stippled_p = s->face->stipple != 0;
+ }
+ 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;
+ }
+ 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);
+
+ if (n > 0)
+ {
+ for (int i = 0; i < n; i++)
+ {
+ cairo_rectangle (cr, r[i].x, r[i].y, r[i].width, r[i].height);
+ }
+ cairo_clip (cr);
+ }
+}
+
+
+/* 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)
+{
+ /* 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)
+ {
+ int box_line_width = max (s->face->box_horizontal_line_width, 0);
+
+ 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)
+ {
+ 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)
+ {
+ if (s->face->id == TAB_BAR_FACE_ID)
+ thick = (tab_bar_button_relief < 0
+ ? DEFAULT_TAB_BAR_BUTTON_RELIEF
+ : min (tab_bar_button_relief, 1000000));
+ else
+ thick = (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)) - thick;
+ extra_y = XFIXNUM (XCDR (Vtab_bar_button_margin)) - thick;
+ }
+ else if (FIXNUMP (Vtab_bar_button_margin))
+ extra_x = extra_y = XFIXNUM (Vtab_bar_button_margin) - thick;
+ }
+
+ if (s->face->id == TOOL_BAR_FACE_ID)
+ {
+ if (CONSP (Vtool_bar_button_margin)
+ && FIXNUMP (XCAR (Vtool_bar_button_margin))
+ && FIXNUMP (XCDR (Vtool_bar_button_margin)))
+ {
+ extra_x = XFIXNUM (XCAR (Vtool_bar_button_margin));
+ extra_y = XFIXNUM (XCDR (Vtool_bar_button_margin));
+ }
+ else if (FIXNUMP (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->background;
+
+ 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)
+{
+ 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);
+ 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. */
+ 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))
+
+ {
+ 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. */
+ x_set_glyph_string_clipping_exactly (s, s, cr);
+ else
+ x_set_glyph_string_clipping (s, cr);
+
+ switch (s->first_glyph->type)
+ {
+ case IMAGE_GLYPH:
+ x_draw_image_glyph_string (s);
+ break;
+
+ case XWIDGET_GLYPH:
+ x_draw_xwidget_glyph_string (s);
+ break;
+
+ case STRETCH_GLYPH:
+ x_draw_stretch_glyph_string (s);
+ break;
+
+ case CHAR_GLYPH:
+ 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:
+ 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:
+ 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 relief if not yet drawn. */
+ if (!relief_drawn_p && s->face->box != FACE_NO_BOX)
+ x_draw_glyph_string_box (s);
+
+ /* 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);
+ }
+ }
+
+ 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;
+ 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;
+ 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)
+{
+ 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_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);
+ 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)
+{
+ 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);
+}
+
+/* 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 frame F. This function is installed as a hook in
+ update_end. */
+
+static void
+pgtk_update_end (struct frame *f)
+{
+ /* Mouse highlight may be displayed again. */
+ MOUSE_HL_INFO (f)->mouse_face_defer = false;
+}
+
+static void
+pgtk_frame_up_to_date (struct frame *f)
+{
+ block_input ();
+ FRAME_MOUSE_UPDATE (f);
+ if (!buffer_flipping_blocked_p ())
+ {
+ flip_cr_context (f);
+ gtk_widget_queue_draw (FRAME_GTK_WIDGET (f));
+ }
+ unblock_input ();
+}
+
+/* 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 ();
+ }
+ }
+
+ /* f1 can be a terminal frame. Bug#50322 */
+ if (f1 == NULL || !FRAME_PGTK_P (f1))
+ {
+ unblock_input ();
+ return;
+ }
+
+ /* 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);
+
+ 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)
+{
+ struct frame *f = XFRAME (WINDOW_FRAME (w));
+ struct face *face = p->face;
+
+ cairo_t *cr = pgtk_begin_cr_clip (f);
+
+ /* 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);
+ }
+ }
+
+ if (p->which && p->which < max_fringe_bmp)
+ {
+ Emacs_GC gcv;
+
+ 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);
+ }
+
+ pgtk_end_cr_clip (f);
+}
+
+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,
+ NULL, /* gui_update_window_begin, */
+ NULL, /* gui_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)
+{
+}
+
+void
+pgtk_clear_frame (struct frame *f)
+/* --------------------------------------------------------------------------
+ External (hook): Erase the entire 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);
+
+ {
+ /* 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))
+ {
+ cairo_rectangle (cr,
+ flash_left,
+ (FRAME_INTERNAL_BORDER_WIDTH (f)
+ + FRAME_TOP_MARGIN_HEIGHT (f)),
+ width, flash_height);
+ cairo_fill (cr);
+
+ cairo_rectangle (cr,
+ flash_left,
+ (height - flash_height
+ - FRAME_INTERNAL_BORDER_WIDTH (f)),
+ width, flash_height);
+ cairo_fill (cr);
+ }
+ else
+ {
+ /* If it is short, flash it all. */
+ cairo_rectangle (cr,
+ flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
+ width, height - 2 * FRAME_INTERNAL_BORDER_WIDTH (f));
+ cairo_fill (cr);
+ }
+
+ 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);
+ }
+
+ }
+
+ 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)
+{
+ GMainContext *context;
+ bool context_acquired = false;
+ int count;
+
+ count = evq_flush (hold_quit);
+ if (count > 0)
+ {
+ return count;
+ }
+
+ context = g_main_context_default ();
+ context_acquired = g_main_context_acquire (context);
+
+ block_input ();
+
+ if (context_acquired)
+ {
+ while (g_main_context_pending (context))
+ {
+ g_main_context_dispatch (context);
+ }
+ }
+
+ unblock_input ();
+
+ if (context_acquired)
+ g_main_context_release (context);
+
+ count = evq_flush (hold_quit);
+ if (count > 0)
+ {
+ return count;
+ }
+
+ return 0;
+}
+
+/* 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));
+
+ if (xg_ignore_gtk_scrollbar)
+ return false;
+
+ 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;
+ }
+
+ 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;
+ 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);
+
+ /* 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:
+ gtk_window_unfullscreen (widget);
+ gtk_window_unmaximize (widget);
+ store_frame_param (f, Qfullscreen, Qnil);
+ break;
+
+ case FULLSCREEN_BOTH:
+ gtk_window_unmaximize (widget);
+ gtk_window_fullscreen (widget);
+ store_frame_param (f, Qfullscreen, Qfullboth);
+ break;
+
+ case FULLSCREEN_MAXIMIZED:
+ gtk_window_unfullscreen (widget);
+ gtk_window_maximize (widget);
+ store_frame_param (f, Qfullscreen, Qmaximized);
+ break;
+
+ case FULLSCREEN_WIDTH:
+ case FULLSCREEN_HEIGHT:
+ /* Not supported by gtk. Ignore them. */
+ break;
+ }
+
+ f->want_fullscreen = FULLSCREEN_NONE;
+}
+
+static void
+pgtk_fullscreen_hook (struct frame *f)
+{
+ 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;
+ }
+
+ if (dpyinfo->connection >= 0)
+ emacs_close (dpyinfo->connection);
+
+ dpyinfo->connection = -1;
+
+ 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_WIDGET (f);
+
+ if (dpyinfo->x_focus_frame != f && wid != NULL)
+ {
+ block_input ();
+ gtk_widget_grab_focus (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 void
+pgtk_buffer_flipping_unblocked_hook (struct frame *f)
+{
+ block_input ();
+ flip_cr_context (f);
+ gtk_widget_queue_draw (FRAME_GTK_WIDGET (f));
+ unblock_input ();
+}
+
+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->buffer_flipping_unblocked_hook = pgtk_buffer_flipping_unblocked_hook;
+ 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)
+{
+ return FALSE;
+}
+
+static void
+pgtk_fill_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, y, width, height);
+ cairo_fill (cr);
+ pgtk_end_cr_clip (f);
+}
+
+void
+pgtk_clear_under_internal_border (struct frame *f)
+{
+ 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 ();
+ }
+}
+
+static gboolean
+pgtk_handle_draw (GtkWidget * widget, cairo_t * cr, gpointer * data)
+{
+ struct frame *f;
+
+ GdkWindow *win = gtk_widget_get_window (widget);
+
+ if (win != NULL)
+ {
+ cairo_surface_t *src = NULL;
+ f = pgtk_any_window_to_frame (win);
+ 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));
+ }
+ if (src != NULL)
+ {
+ cairo_set_source_surface (cr, src, 0, 0);
+ cairo_paint (cr);
+ }
+ }
+ return FALSE;
+}
+
+static void
+size_allocate (GtkWidget * widget, GtkAllocation * alloc,
+ gpointer user_data)
+{
+ struct frame *f = pgtk_any_window_to_frame (gtk_widget_get_window (widget));
+
+ /* Between a frame is created and not shown, size is allocated and
+ * this handler is called. When that, since the widget's window is
+ * NULL, we can't get f, pgtk_cr_update_surface_desired_size is not
+ * called, and its size is 0x0. That causes empty frame.
+ *
+ * Fortunately since we know f in pgtk_set_event_handler, we can get
+ * it through user_data;
+ */
+ if (!f)
+ f = user_data;
+
+ if (f)
+ {
+ xg_frame_resized (f, alloc->width, alloc->height);
+ pgtk_cr_update_surface_desired_size (f, alloc->width, alloc->height, false);
+ }
+}
+
+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;
+ }
+
+ /* If xmodmap says:
+ * $ xmodmap | grep mod4
+ * mod4 Super_L (0x85), Super_R (0x86), Super_L (0xce), Hyper_L (0xcf)
+ * then, when mod4 is pressed, both of super and hyper are recognized ON.
+ * Maybe many people have such configuration, and they don't like such behavior,
+ * so I disable hyper if such configuration is detected.
+ */
+ if (dpyinfo->hyper_mod_mask == dpyinfo->super_mod_mask)
+ dpyinfo->hyper_mod_mask = 0;
+}
+
+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;
+
+ 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)
+{
+ 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;
+
+ 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;
+
+ 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)
+{
+ 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)
+{
+ 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;
+ Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (frame);
+
+ if (frame == hlinfo->mouse_face_mouse_frame)
+ {
+ /* If we move outside the frame, then we're
+ certainly no longer on any text in the frame. */
+ clear_mouse_face (hlinfo);
+ hlinfo->mouse_face_mouse_frame = 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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;
+ Lisp_Object tab_bar_arg = Qnil;
+
+ 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 ();
+ gtk_widget_grab_focus (FRAME_GTK_WIDGET (f));
+
+ if (FRAME_GTK_OUTER_WIDGET (f))
+ gtk_window_present (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
+ 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)
+ tab_bar_arg = 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 && NILP (tab_bar_arg)) && !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 (!NILP (tab_bar_arg))
+ inev.ie.arg = tab_bar_arg;
+ }
+#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)
+{
+ 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_is_scroll_stop_event (event))
+ {
+ inev.ie.kind = TOUCH_END_EVENT;
+ evq_enqueue (&inev);
+ return TRUE;
+ }
+
+ 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
+ || !mwheel_coalesce_scroll_events)
+ {
+ int nlines = dpyinfo->scroll.acc_y / dpyinfo->scroll.y_per_line;
+ inev.ie.kind = WHEEL_EVENT;
+ inev.ie.modifiers |= down_modifier;
+ inev.ie.arg = list3 (make_fixnum (nlines),
+ make_float (-dpyinfo->scroll.acc_x * 100),
+ make_float (-dpyinfo->scroll.acc_y * 100));
+ if (!mwheel_coalesce_scroll_events)
+ {
+ dpyinfo->scroll.acc_y = 0;
+ dpyinfo->scroll.acc_x = 0;
+ }
+ else
+ {
+ dpyinfo->scroll.acc_y -= dpyinfo->scroll.y_per_line * nlines;
+ }
+ }
+ else if (dpyinfo->scroll.acc_y <= -dpyinfo->scroll.y_per_line
+ || !mwheel_coalesce_scroll_events)
+ {
+ int nlines = -dpyinfo->scroll.acc_y / dpyinfo->scroll.y_per_line;
+ inev.ie.kind = WHEEL_EVENT;
+ inev.ie.modifiers |= up_modifier;
+ inev.ie.arg = list3 (make_fixnum (nlines),
+ make_float (-dpyinfo->scroll.acc_x * 100),
+ make_float (-dpyinfo->scroll.acc_y * 100));
+
+ if (!mwheel_coalesce_scroll_events)
+ {
+ dpyinfo->scroll.acc_y = 0;
+ dpyinfo->scroll.acc_x = 0;
+ }
+ else
+ dpyinfo->scroll.acc_y -= -dpyinfo->scroll.y_per_line * nlines;
+ }
+ else if (dpyinfo->scroll.acc_x >= dpyinfo->scroll.x_per_char
+ || !mwheel_coalesce_scroll_events)
+ {
+ 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 = list3 (make_fixnum (nchars),
+ make_float (-dpyinfo->scroll.acc_x * 100),
+ make_float (-dpyinfo->scroll.acc_y * 100));
+
+ if (mwheel_coalesce_scroll_events)
+ dpyinfo->scroll.acc_x -= dpyinfo->scroll.x_per_char * nchars;
+ else
+ {
+ dpyinfo->scroll.acc_x = 0;
+ dpyinfo->scroll.acc_y = 0;
+ }
+ }
+ 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 = list3 (make_fixnum (nchars),
+ make_float (-dpyinfo->scroll.acc_x * 100),
+ make_float (-dpyinfo->scroll.acc_y * 100));
+
+ if (mwheel_coalesce_scroll_events)
+ dpyinfo->scroll.acc_x -= -dpyinfo->scroll.x_per_char * nchars;
+ else
+ {
+ dpyinfo->scroll.acc_x = 0;
+ dpyinfo->scroll.acc_y = 0;
+ }
+ }
+ }
+
+ 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)
+{
+ 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;
+
+ 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);
+ }
+ }
+
+ 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), f);
+ 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;
+ static dynlib_handle_ptr *handle = 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 = gdk_screen_get_resolution (gscr);
+ if (dpi < 0)
+ dpi = 96.0;
+
+ dpi *= pgtk_text_scaling_factor ();
+ dpyinfo->resx = dpi;
+ dpyinfo->resy = dpi;
+ }
+
+ /* smooth scroll setting */
+ dpyinfo->scroll.x_per_char = 2;
+ dpyinfo->scroll.y_per_line = 2;
+
+ dpyinfo->connection = -1;
+
+ if (!handle)
+ handle = dynlib_open (NULL);
+
+#ifdef GDK_WINDOWING_X11
+ if (!strcmp (G_OBJECT_TYPE_NAME (dpy), "GdkX11Display") && handle)
+ {
+ void *(*gdk_x11_display_get_xdisplay) (GdkDisplay *)
+ = dynlib_sym (handle, "gdk_x11_display_get_xdisplay");
+ int (*x_connection_number) (void *)
+ = dynlib_sym (handle, "XConnectionNumber");
+
+ if (x_connection_number
+ && gdk_x11_display_get_xdisplay)
+ dpyinfo->connection
+ = x_connection_number (gdk_x11_display_get_xdisplay (dpy));
+ }
+#endif
+
+#ifdef GDK_WINDOWING_WAYLAND
+ if (GDK_IS_WAYLAND_DISPLAY (dpy) && handle)
+ {
+ struct wl_display *wl_dpy = gdk_wayland_display_get_wl_display (dpy);
+ int (*display_get_fd) (struct wl_display *)
+ = dynlib_sym (handle, "wl_display_get_fd");
+
+ if (display_get_fd)
+ dpyinfo->connection = display_get_fd (wl_dpy);
+ }
+#endif
+
+ if (dpyinfo->connection >= 0)
+ {
+ add_keyboard_wait_descriptor (dpyinfo->connection);
+#ifdef F_SETOWN
+ fcntl (dpyinfo->connection, F_SETOWN, getpid ());
+#endif /* ! defined (F_SETOWN) */
+
+ if (interrupt_input)
+ init_sigio (dpyinfo->connection);
+ }
+
+ 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.
+ -------------------------------------------------------------------------- */
+{
+ 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");
+
+ 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
+ -------------------------------------------------------------------------- */
+{
+ 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)
+{
+ 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)
+{
+ int i;
+
+ for (i = 0; i < ncolors; i++)
+ {
+ unsigned long pixel = colors[i].pixel;
+ /* Convert to a 16 bit value in range 0 - 0xffff. */
+#define GetRValue(p) (((p) >> 16) & 0xff)
+#define GetGValue(p) (((p) >> 8) & 0xff)
+#define GetBValue(p) (((p) >> 0) & 0xff)
+ colors[i].red = GetRValue (pixel) * 257;
+ colors[i].green = GetGValue (pixel) * 257;
+ colors[i].blue = GetBValue (pixel) * 257;
+ }
+}
+
+void
+pgtk_query_color (struct frame *f, Emacs_Color * color)
+{
+ pgtk_query_colors (f, color, 1);
+}
+
+void
+pgtk_clear_area (struct frame *f, int x, int y, int width, int height)
+{
+ cairo_t *cr;
+
+ eassert (width > 0 && height > 0);
+
+ cr = pgtk_begin_cr_clip (f);
+ 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, bool force)
+{
+ if (FRAME_CR_SURFACE_DESIRED_WIDTH (f) != width
+ || FRAME_CR_SURFACE_DESIRED_HEIGHT (f) != height
+ || force)
+ {
+ 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);
+
+ 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)
+{
+ cairo_restore (FRAME_CR_CONTEXT (f));
+}
+
+void
+pgtk_set_cr_source_with_gc_foreground (struct frame *f, Emacs_GC * gc)
+{
+ pgtk_set_cr_source_with_color (f, gc->foreground);
+}
+
+void
+pgtk_set_cr_source_with_gc_background (struct frame *f, Emacs_GC * gc)
+{
+ pgtk_set_cr_source_with_color (f, gc->background);
+}
+
+void
+pgtk_set_cr_source_with_color (struct frame *f, unsigned long 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)
+{
+ 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)
+{
+ 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 --git a/src/pgtkterm.h b/src/pgtkterm.h
new file mode 100644
index 00000000000..e76411cf021
--- /dev/null
+++ b/src/pgtkterm.h
@@ -0,0 +1,664 @@
+/* Definitions and headers for communication with pure Gtk+3.
+ Copyright (C) 1989, 1993, 2005, 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/>. */
+
+
+#include "dispextern.h"
+#include "frame.h"
+#include "character.h"
+#include "font.h"
+#include "sysselect.h"
+
+#ifdef HAVE_PGTK
+
+#include <gtk/gtk.h>
+
+#ifdef CAIRO_HAS_PDF_SURFACE
+#include <cairo-pdf.h>
+#endif
+#ifdef CAIRO_HAS_PS_SURFACE
+#include <cairo-ps.h>
+#endif
+#ifdef CAIRO_HAS_SVG_SURFACE
+#include <cairo-svg.h>
+#endif
+
+/* could use list to store these, but rest of emacs has a big infrastructure
+ for managing a table of bitmap "records" */
+struct pgtk_bitmap_record
+{
+ void *img;
+ char *file;
+ int refcount;
+ int height, width, depth;
+ cairo_pattern_t *pattern;
+};
+
+#define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b))
+#define ARGB_TO_ULONG(a, r, g, b) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
+
+#define ALPHA_FROM_ULONG(color) ((color) >> 24)
+#define RED_FROM_ULONG(color) (((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color) (((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color) ((color) & 0xff)
+
+struct scroll_bar
+{
+ /* These fields are shared by all vectors. */
+ union vectorlike_header header;
+
+ /* The window we're a scroll bar for. */
+ Lisp_Object window;
+
+ /* The next and previous in the chain of scroll bars in this frame. */
+ Lisp_Object next, prev;
+
+ /* Fields from `x_window' down will not be traced by the GC. */
+
+ /* The X window representing this scroll bar. */
+ Window x_window;
+
+ /* The position and size of the scroll bar in pixels, relative to the
+ frame. */
+ int top, left, width, height;
+
+ /* The starting and ending positions of the handle, relative to the
+ handle area (i.e. zero is the top position, not
+ SCROLL_BAR_TOP_BORDER). If they're equal, that means the handle
+ hasn't been drawn yet.
+
+ These are not actually the locations where the beginning and end
+ are drawn; in order to keep handles from becoming invisible when
+ editing large files, we establish a minimum height by always
+ drawing handle bottoms VERTICAL_SCROLL_BAR_MIN_HANDLE pixels below
+ where they would be normally; the bottom and top are in a
+ different co-ordinate system. */
+ int start, end;
+
+ /* If the scroll bar handle is currently being dragged by the user,
+ this is the number of pixels from the top of the handle to the
+ place where the user grabbed it. If the handle isn't currently
+ being dragged, this is -1. */
+ int dragging;
+
+#if defined (USE_TOOLKIT_SCROLL_BARS) && defined (USE_LUCID)
+ /* Last scroll bar part seen in xaw_jump_callback and xaw_scroll_callback. */
+ enum scroll_bar_part last_seen_part;
+#endif
+
+#if defined (USE_TOOLKIT_SCROLL_BARS) && !defined (USE_GTK)
+ /* Last value of whole for horizontal scrollbars. */
+ int whole;
+#endif
+
+ /* True if the scroll bar is horizontal. */
+ bool horizontal;
+};
+
+
+/* init'd in pgtk_initialize_display_info () */
+struct pgtk_display_info
+{
+ /* Chain of all pgtk_display_info structures. */
+ struct pgtk_display_info *next;
+
+ /* The generic display parameters corresponding to this PGTK display. */
+ struct terminal *terminal;
+
+ /* This says how to access this display in Gdk. */
+ GdkDisplay *gdpy;
+
+ /* This is a cons cell of the form (NAME . FONT-LIST-CACHE). */
+ Lisp_Object name_list_element;
+
+ /* Number of frames that are on this display. */
+ int reference_count;
+
+ /* Logical identifier of this display. */
+ unsigned x_id;
+
+ /* Default name for all frames on this display. */
+ char *x_id_name;
+
+ /* The number of fonts loaded. */
+ int n_fonts;
+
+ /* Minimum width over all characters in all fonts in font_table. */
+ int smallest_char_width;
+
+ /* Minimum font height over all fonts in font_table. */
+ int smallest_font_height;
+
+ struct pgtk_bitmap_record *bitmaps;
+ ptrdiff_t bitmaps_size;
+ ptrdiff_t bitmaps_last;
+
+ /* DPI resolution of this screen */
+ double resx, resy;
+
+ /* Mask of things that cause the mouse to be grabbed */
+ int grabbed;
+
+ int n_planes;
+
+ int color_p;
+
+ /* Emacs bitmap-id of the default icon bitmap for this frame.
+ Or -1 if none has been allocated yet. */
+ ptrdiff_t icon_bitmap_id;
+
+ Window root_window;
+
+ /* Xism */
+ XrmDatabase rdb;
+
+ /* The cursor to use for vertical scroll bars. */
+ Emacs_Cursor vertical_scroll_bar_cursor;
+
+ /* The cursor to use for horizontal scroll bars. */
+ Emacs_Cursor horizontal_scroll_bar_cursor;
+
+ /* Information about the range of text currently shown in
+ mouse-face. */
+ Mouse_HLInfo mouse_highlight;
+
+ struct frame *highlight_frame;
+ struct frame *x_focus_frame;
+
+ /* The last frame mentioned in a FocusIn or FocusOut event. This is
+ separate from x_focus_frame, because whether or not LeaveNotify
+ events cause us to lose focus depends on whether or not we have
+ received a FocusIn event for it. */
+ struct frame *x_focus_event_frame;
+
+ /* The frame where the mouse was last time we reported a mouse event. */
+ struct frame *last_mouse_frame;
+
+ /* The frame where the mouse was last time we reported a mouse motion. */
+ struct frame *last_mouse_motion_frame;
+
+ /* Position where the mouse was last time we reported a motion.
+ This is a position on last_mouse_motion_frame. */
+ int last_mouse_motion_x;
+ int last_mouse_motion_y;
+
+ /* Where the mouse was last time we reported a mouse position. */
+ XRectangle last_mouse_glyph;
+
+ /* Time of last mouse movement. */
+ Time last_mouse_movement_time;
+
+ /* The scroll bar in which the last motion event occurred. */
+ void *last_mouse_scroll_bar;
+
+ /* The invisible cursor used for pointer blanking.
+ Unused if this display supports Xfixes extension. */
+ Emacs_Cursor invisible_cursor;
+
+ /* Function used to toggle pointer visibility on this display. */
+ void (*toggle_visible_pointer) (struct frame *, bool);
+
+ /* The GDK cursor for scroll bars and popup menus. */
+ GdkCursor *xg_cursor;
+
+
+ /* The frame where the mouse was last time we reported a mouse position. */
+ struct frame *last_mouse_glyph_frame;
+
+ /* Modifier masks in gdk */
+ int meta_mod_mask, alt_mod_mask, super_mod_mask, hyper_mod_mask;
+
+ /* The last click event. */
+ GdkEvent *last_click_event;
+
+ /* input method */
+ struct
+ {
+ GtkIMContext *context;
+ struct frame *focused_frame;
+ } im;
+
+ struct
+ {
+ double acc_x, acc_y;
+ double x_per_char, y_per_line;
+ } scroll;
+
+ int connection;
+};
+
+/* This is a chain of structures for all the PGTK displays currently in use. */
+extern struct pgtk_display_info *x_display_list;
+
+struct pgtk_output
+{
+#if 0
+ void *view;
+ void *miniimage;
+#endif
+ unsigned long foreground_color;
+ unsigned long background_color;
+ void *toolbar;
+
+ /* Cursors */
+ Emacs_Cursor current_cursor;
+ Emacs_Cursor text_cursor;
+ Emacs_Cursor nontext_cursor;
+ Emacs_Cursor modeline_cursor;
+ Emacs_Cursor hand_cursor;
+ Emacs_Cursor hourglass_cursor;
+ Emacs_Cursor horizontal_drag_cursor;
+ Emacs_Cursor vertical_drag_cursor;
+ Emacs_Cursor left_edge_cursor;
+ Emacs_Cursor top_left_corner_cursor;
+ Emacs_Cursor top_edge_cursor;
+ Emacs_Cursor top_right_corner_cursor;
+ Emacs_Cursor right_edge_cursor;
+ Emacs_Cursor bottom_right_corner_cursor;
+ Emacs_Cursor bottom_edge_cursor;
+ Emacs_Cursor bottom_left_corner_cursor;
+
+ /* PGTK-specific */
+ Emacs_Cursor current_pointer;
+
+ /* border color */
+ unsigned long border_pixel;
+ GtkCssProvider *border_color_css_provider;
+
+ /* scrollbar color */
+ GtkCssProvider *scrollbar_foreground_css_provider;
+ GtkCssProvider *scrollbar_background_css_provider;
+
+ /* Widget whose cursor is hourglass_cursor. This widget is temporarily
+ mapped to display an hourglass cursor. */
+ GtkWidget *hourglass_widget;
+
+ Emacs_GC cursor_xgcv;
+
+ /* lord knows why Emacs needs to know about our Window ids.. */
+ Window window_desc, parent_desc;
+ char explicit_parent;
+
+ /* If >=0, a bitmap index. The indicated bitmap is used for the
+ icon. */
+ ptrdiff_t icon_bitmap;
+
+ struct font *font;
+ int baseline_offset;
+
+ /* If a fontset is specified for this frame instead of font, this
+ value contains an ID of the fontset, else -1. */
+ int fontset; /* only used with font_backend */
+
+ unsigned long mouse_color;
+ unsigned long cursor_color;
+ unsigned long cursor_foreground_color;
+
+ int icon_top;
+ int icon_left;
+
+ /* The size of the extra width currently allotted for vertical
+ scroll bars, in pixels. */
+ int vertical_scroll_bar_extra;
+
+ /* The height of the titlebar decoration (included in PGTKWindow's frame). */
+ int titlebar_height;
+
+ /* The height of the toolbar if displayed, else 0. */
+ int toolbar_height;
+
+ /* This is the Emacs structure for the PGTK display this frame is on. */
+ struct pgtk_display_info *display_info;
+
+ /* Non-zero if we are zooming (maximizing) the frame. */
+ int zooming;
+
+ /* Non-zero if we are doing an animation, e.g. toggling the tool bar. */
+ int in_animation;
+
+ /* The last size hints set. */
+ GdkGeometry size_hints;
+ long hint_flags;
+ int preferred_width, preferred_height;
+
+ /* The widget of this screen. This is the window of a top widget. */
+ GtkWidget *widget;
+ /* The widget of the edit portion of this screen; the window in
+ "window_desc" is inside of this. */
+ GtkWidget *edit_widget;
+ /* The widget used for laying out widgets vertically. */
+ GtkWidget *vbox_widget;
+ /* The widget used for laying out widgets horizontally. */
+ GtkWidget *hbox_widget;
+ /* The menubar in this frame. */
+ GtkWidget *menubar_widget;
+ /* The tool bar in this frame */
+ GtkWidget *toolbar_widget;
+ /* True if tool bar is packed into the hbox widget (i.e. vertical). */
+ bool_bf toolbar_in_hbox:1;
+ bool_bf toolbar_is_packed:1;
+
+ GtkTooltip *ttip_widget;
+ GtkWidget *ttip_lbl;
+ GtkWindow *ttip_window;
+
+ /* Height of menu bar widget, in pixels. This value
+ is not meaningful if the menubar is turned off. */
+ int menubar_height;
+
+ /* Height of tool bar widget, in pixels. top_height is used if tool bar
+ at top, bottom_height if tool bar is at the bottom.
+ Zero if not using an external tool bar or if tool bar is vertical. */
+ int toolbar_top_height, toolbar_bottom_height;
+
+ /* Width of tool bar widget, in pixels. left_width is used if tool bar
+ at left, right_width if tool bar is at the right.
+ Zero if not using an external tool bar or if tool bar is horizontal. */
+ int toolbar_left_width, toolbar_right_width;
+
+#ifdef USE_CAIRO
+ /* Cairo drawing contexts. */
+ cairo_t *cr_context, *cr_active;
+ int cr_surface_desired_width, cr_surface_desired_height;
+ /* Cairo surface for double buffering */
+ cairo_surface_t *cr_surface_visible_bell;
+#endif
+ struct atimer *atimer_visible_bell;
+
+ int has_been_visible;
+
+ /* Relief GCs, colors etc. */
+ struct relief
+ {
+ Emacs_GC xgcv;
+ unsigned long pixel;
+ }
+ black_relief, white_relief;
+
+ /* The background for which the above relief GCs were set up.
+ They are changed only when a different background is involved. */
+ unsigned long relief_background;
+
+ /* Keep track of focus. May be EXPLICIT if we received a FocusIn for this
+ frame, or IMPLICIT if we received an EnterNotify.
+ FocusOut and LeaveNotify clears EXPLICIT/IMPLICIT. */
+ int focus_state;
+
+ /* Keep track of scale factor. If monitor's scale factor is changed, or
+ monitor is switched and scale factor is changed, then recreate cairo_t
+ and cairo_surface_t. I need GTK's such signal, but there isn't, so
+ I watch it periodically with atimer. */
+ double watched_scale_factor;
+ struct atimer *scale_factor_atimer;
+};
+
+/* this dummy decl needed to support TTYs */
+struct x_output
+{
+ int unused;
+};
+
+enum
+{
+ /* Values for focus_state, used as bit mask.
+ EXPLICIT means we received a FocusIn for the frame and know it has
+ the focus. IMPLICIT means we received an EnterNotify and the frame
+ may have the focus if no window manager is running.
+ FocusOut and LeaveNotify clears EXPLICIT/IMPLICIT. */
+ FOCUS_NONE = 0,
+ FOCUS_IMPLICIT = 1,
+ FOCUS_EXPLICIT = 2
+};
+
+/* This gives the pgtk_display_info structure for the display F is on. */
+#define FRAME_X_OUTPUT(f) ((f)->output_data.pgtk)
+#define FRAME_OUTPUT_DATA(f) FRAME_X_OUTPUT (f)
+
+#define FRAME_DISPLAY_INFO(f) (FRAME_X_OUTPUT (f)->display_info)
+#define FRAME_FOREGROUND_COLOR(f) (FRAME_X_OUTPUT (f)->foreground_color)
+#define FRAME_BACKGROUND_COLOR(f) (FRAME_X_OUTPUT (f)->background_color)
+#define FRAME_CURSOR_COLOR(f) (FRAME_X_OUTPUT (f)->cursor_color)
+#define FRAME_POINTER_TYPE(f) (FRAME_X_OUTPUT (f)->current_pointer)
+#define FRAME_FONT(f) (FRAME_X_OUTPUT (f)->font)
+#define FRAME_GTK_OUTER_WIDGET(f) (FRAME_X_OUTPUT (f)->widget)
+#define FRAME_GTK_WIDGET(f) (FRAME_X_OUTPUT (f)->edit_widget)
+#define FRAME_WIDGET(f) (FRAME_GTK_OUTER_WIDGET (f) ? \
+ FRAME_GTK_OUTER_WIDGET (f) : \
+ FRAME_GTK_WIDGET (f))
+
+/* aliases */
+#define FRAME_PGTK_VIEW(f) FRAME_GTK_WIDGET (f)
+#define FRAME_X_WINDOW(f) FRAME_GTK_OUTER_WIDGET (f)
+#define FRAME_NATIVE_WINDOW(f) GTK_WINDOW (FRAME_X_WINDOW (f))
+
+#define FRAME_X_DISPLAY(f) (FRAME_DISPLAY_INFO (f)->gdpy)
+
+#define DEFAULT_GDK_DISPLAY() gdk_display_get_default ()
+
+/* Turning a lisp vector value into a pointer to a struct scroll_bar. */
+#define XSCROLL_BAR(vec) ((struct scroll_bar *) XVECTOR (vec))
+
+#define PGTK_FACE_FOREGROUND(f) ((f)->foreground)
+#define PGTK_FACE_BACKGROUND(f) ((f)->background)
+#define FRAME_DEFAULT_FACE(f) FACE_FROM_ID_OR_NULL (f, DEFAULT_FACE_ID)
+
+/* Compute pixel height of the frame's titlebar. */
+#define FRAME_PGTK_TITLEBAR_HEIGHT(f) 0
+
+/* Compute pixel size for vertical scroll bars */
+#define PGTK_SCROLL_BAR_WIDTH(f) \
+ (FRAME_HAS_VERTICAL_SCROLL_BARS (f) \
+ ? rint (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0 \
+ ? FRAME_CONFIG_SCROLL_BAR_WIDTH (f) \
+ : (FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f))) \
+ : 0)
+
+/* Compute pixel size for horizontal scroll bars */
+#define PGTK_SCROLL_BAR_HEIGHT(f) \
+ (FRAME_HAS_HORIZONTAL_SCROLL_BARS (f) \
+ ? rint (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) > 0 \
+ ? FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) \
+ : (FRAME_SCROLL_BAR_LINES (f) * FRAME_LINE_HEIGHT (f))) \
+ : 0)
+
+/* Difference btwn char-column-calculated and actual SB widths.
+ This is only a concern for rendering when SB on left. */
+#define PGTK_SCROLL_BAR_ADJUST(w, f) \
+ (WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_LEFT (w) ? \
+ (FRAME_SCROLL_BAR_COLS (f) * FRAME_COLUMN_WIDTH (f) \
+ - PGTK_SCROLL_BAR_WIDTH (f)) : 0)
+
+/* Difference btwn char-line-calculated and actual SB heights.
+ This is only a concern for rendering when SB on top. */
+#define PGTK_SCROLL_BAR_ADJUST_HORIZONTALLY(w, f) \
+ (WINDOW_HAS_HORIZONTAL_SCROLL_BARS (w) ? \
+ (FRAME_SCROLL_BAR_LINES (f) * FRAME_LINE_HEIGHT (f) \
+ - PGTK_SCROLL_BAR_HEIGHT (f)) : 0)
+
+#define FRAME_MENUBAR_HEIGHT(f) (FRAME_X_OUTPUT (f)->menubar_height)
+
+/* Calculate system coordinates of the left and top of the parent
+ window or, if there is no parent window, the screen. */
+#define PGTK_PARENT_WINDOW_LEFT_POS(f) \
+ (FRAME_PARENT_FRAME (f) != NULL \
+ ? [[FRAME_PGTK_VIEW (f) window] parentWindow].frame.origin.x : 0)
+#define PGTK_PARENT_WINDOW_TOP_POS(f) \
+ (FRAME_PARENT_FRAME (f) != NULL \
+ ? ([[FRAME_PGTK_VIEW (f) window] parentWindow].frame.origin.y \
+ + [[FRAME_PGTK_VIEW (f) window] parentWindow].frame.size.height \
+ - FRAME_PGTK_TITLEBAR_HEIGHT (FRAME_PARENT_FRAME (f))) \
+ : [[[PGTKScreen screepgtk] objectAtIndex: 0] frame].size.height)
+
+#define FRAME_PGTK_FONT_TABLE(f) (FRAME_DISPLAY_INFO (f)->font_table)
+
+#define FRAME_TOOLBAR_TOP_HEIGHT(f) ((f)->output_data.pgtk->toolbar_top_height)
+#define FRAME_TOOLBAR_BOTTOM_HEIGHT(f) \
+ ((f)->output_data.pgtk->toolbar_bottom_height)
+#define FRAME_TOOLBAR_HEIGHT(f) \
+ (FRAME_TOOLBAR_TOP_HEIGHT (f) + FRAME_TOOLBAR_BOTTOM_HEIGHT (f))
+#define FRAME_TOOLBAR_LEFT_WIDTH(f) ((f)->output_data.pgtk->toolbar_left_width)
+#define FRAME_TOOLBAR_RIGHT_WIDTH(f) ((f)->output_data.pgtk->toolbar_right_width)
+#define FRAME_TOOLBAR_WIDTH(f) \
+ (FRAME_TOOLBAR_LEFT_WIDTH (f) + FRAME_TOOLBAR_RIGHT_WIDTH (f))
+
+#define FRAME_FONTSET(f) (FRAME_X_OUTPUT (f)->fontset)
+
+#define FRAME_BASELINE_OFFSET(f) (FRAME_X_OUTPUT (f)->baseline_offset)
+#define BLACK_PIX_DEFAULT(f) 0x000000
+#define WHITE_PIX_DEFAULT(f) 0xFFFFFF
+
+/* First position where characters can be shown (instead of scrollbar, if
+ it is on left. */
+#define FIRST_CHAR_POSITION(f) \
+ (! (FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT (f)) ? 0 \
+ : FRAME_SCROLL_BAR_COLS (f))
+
+#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)
+
+/* Display init/shutdown functions implemented in pgtkterm.c */
+extern struct pgtk_display_info *pgtk_term_init (Lisp_Object display_name,
+ char *resource_name);
+extern void pgtk_term_shutdown (int sig);
+
+/* Implemented in pgtkterm, published in or needed from pgtkfns. */
+extern void pgtk_clear_frame (struct frame *f);
+extern char *pgtk_xlfd_to_fontname (const char *xlfd);
+
+/* Implemented in pgtkfns. */
+extern void pgtk_set_doc_edited (void);
+extern const char *pgtk_get_defaults_value (const char *key);
+extern const char *pgtk_get_string_resource (XrmDatabase rdb,
+ const char *name,
+ const char *class);
+extern void pgtk_implicitly_set_name (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval);
+
+/* Color management implemented in pgtkterm. */
+extern bool pgtk_defined_color (struct frame *f,
+ const char *name,
+ Emacs_Color * color_def, bool alloc,
+ bool makeIndex);
+extern void pgtk_query_color (struct frame *f, Emacs_Color * color);
+extern void pgtk_query_colors (struct frame *f, Emacs_Color * colors,
+ int ncolors);
+extern int pgtk_parse_color (struct frame *f, const char *color_name,
+ Emacs_Color * color);
+
+/* Implemented in pgtkterm.c */
+extern void pgtk_clear_area (struct frame *f, int x, int y, int width,
+ int height);
+extern int pgtk_gtk_to_emacs_modifiers (struct pgtk_display_info *dpyinfo,
+ int state);
+extern void pgtk_clear_under_internal_border (struct frame *f);
+extern void pgtk_set_event_handler (struct frame *f);
+
+/* Implemented in pgtkterm.c */
+extern int x_display_pixel_height (struct pgtk_display_info *);
+extern int x_display_pixel_width (struct pgtk_display_info *);
+
+/* Implemented in pgtkterm.c */
+extern void x_destroy_window (struct frame *f);
+extern void x_set_parent_frame (struct frame *f, Lisp_Object new_value,
+ Lisp_Object old_value);
+extern void x_set_no_focus_on_map (struct frame *f, Lisp_Object new_value,
+ Lisp_Object old_value);
+extern void x_set_no_accept_focus (struct frame *f, Lisp_Object new_value,
+ Lisp_Object old_value);
+extern void x_set_z_group (struct frame *f, Lisp_Object new_value,
+ Lisp_Object old_value);
+
+/* Cairo related functions implemented in pgtkterm.c */
+extern void pgtk_cr_update_surface_desired_size (struct frame *, int, int, bool);
+extern cairo_t *pgtk_begin_cr_clip (struct frame *f);
+extern void pgtk_end_cr_clip (struct frame *f);
+extern void pgtk_set_cr_source_with_gc_foreground (struct frame *f,
+ Emacs_GC * gc);
+extern void pgtk_set_cr_source_with_gc_background (struct frame *f,
+ Emacs_GC * gc);
+extern void pgtk_set_cr_source_with_color (struct frame *f,
+ unsigned long color);
+extern void pgtk_cr_draw_frame (cairo_t * cr, struct frame *f);
+extern void pgtk_cr_destroy_frame_context (struct frame *f);
+extern Lisp_Object pgtk_cr_export_frames (Lisp_Object frames, cairo_surface_type_t surface_type);
+
+/* Defined in pgtkmenu.c */
+extern Lisp_Object pgtk_popup_dialog (struct frame *f, Lisp_Object header,
+ Lisp_Object contents);
+extern Lisp_Object pgtk_dialog_show (struct frame *f, Lisp_Object title,
+ Lisp_Object header,
+ const char **error_name);
+extern void initialize_frame_menubar (struct frame *);
+
+
+/* Symbol initializations implemented in each pgtk sources. */
+extern void syms_of_pgtkterm (void);
+extern void syms_of_pgtkfns (void);
+extern void syms_of_pgtkmenu (void);
+extern void syms_of_pgtkselect (void);
+extern void syms_of_pgtkim (void);
+
+/* Implemented in pgtkselect. */
+extern void nxatoms_of_pgtkselect (void);
+
+/* Initialization and marking implemented in pgtkterm.c */
+extern void init_pgtkterm (void);
+extern void mark_pgtkterm (void);
+extern void pgtk_delete_terminal (struct terminal *terminal);
+
+extern void pgtk_make_frame_visible (struct frame *f);
+extern void pgtk_make_frame_invisible (struct frame *f);
+extern void x_wm_set_size_hint (struct frame *, long, bool);
+extern void x_free_frame_resources (struct frame *);
+extern void pgtk_iconify_frame (struct frame *f);
+extern void pgtk_focus_frame (struct frame *f, bool noactivate);
+extern void pgtk_set_scroll_bar_default_width (struct frame *f);
+extern void pgtk_set_scroll_bar_default_height (struct frame *f);
+extern Lisp_Object x_get_focus_frame (struct frame *frame);
+
+extern void pgtk_frame_rehighlight (struct pgtk_display_info *dpyinfo);
+
+extern void x_change_tab_bar_height (struct frame *, int);
+
+extern struct pgtk_display_info *check_pgtk_display_info (Lisp_Object object);
+
+extern void pgtk_default_font_parameter (struct frame *f, Lisp_Object parms);
+
+extern void pgtk_menu_set_in_use (bool in_use);
+
+
+extern void pgtk_enqueue_string (struct frame *f, gchar * str);
+extern void pgtk_enqueue_preedit (struct frame *f, Lisp_Object image_data);
+extern void pgtk_im_focus_in (struct frame *f);
+extern void pgtk_im_focus_out (struct frame *f);
+extern bool pgtk_im_filter_keypress (struct frame *f, GdkEventKey * ev);
+extern void pgtk_im_set_cursor_location (struct frame *f, int x, int y,
+ int width, int height);
+extern void pgtk_im_init (struct pgtk_display_info *dpyinfo);
+extern void pgtk_im_finish (struct pgtk_display_info *dpyinfo);
+
+extern bool xg_set_icon (struct frame *, Lisp_Object);
+extern bool xg_set_icon_from_xpm_data (struct frame *f, const char **data);
+
+extern bool pgtk_text_icon (struct frame *f, const char *icon_name);
+
+extern double pgtk_frame_scale_factor (struct frame *);
+
+#endif /* HAVE_PGTK */
diff --git a/src/termhooks.h b/src/termhooks.h
index 587d1eef91a..9f22187b841 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -61,6 +61,7 @@ enum output_method
output_msdos_raw,
output_w32,
output_ns,
+ output_pgtk,
output_haiku
};
@@ -268,6 +269,11 @@ enum event_kind
, FILE_NOTIFY_EVENT
#endif
+#ifdef HAVE_PGTK
+ /* Pre-edit text was changed. */
+ , PGTK_PREEDIT_TEXT_EVENT
+#endif
+
/* Either the mouse wheel has been released without it being
clicked, or the user has lifted his finger from a touchpad.
@@ -467,6 +473,7 @@ struct terminal
struct x_display_info *x; /* xterm.h */
struct w32_display_info *w32; /* w32term.h */
struct ns_display_info *ns; /* nsterm.h */
+ struct pgtk_display_info *pgtk; /* pgtkterm.h */
struct haiku_display_info *haiku; /* haikuterm.h */
} display_info;
@@ -541,7 +548,7 @@ struct terminal
BGCOLOR. */
void (*query_frame_background_color) (struct frame *f, Emacs_Color *bgcolor);
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI) || defined (HAVE_PGTK)
/* On frame F, translate pixel colors to RGB values for the NCOLORS
colors in COLORS. Use cached information, if available. */
@@ -856,6 +863,9 @@ extern struct terminal *terminal_list;
#elif defined (HAVE_NS)
#define TERMINAL_FONT_CACHE(t) \
(t->type == output_ns ? t->display_info.ns->name_list_element : Qnil)
+#elif defined (HAVE_PGTK)
+#define TERMINAL_FONT_CACHE(t) \
+ (t->type == output_pgtk ? t->display_info.pgtk->name_list_element : Qnil)
#elif defined (HAVE_HAIKU)
#define TERMINAL_FONT_CACHE(t) \
(t->type == output_haiku ? t->display_info.haiku->name_list_element : Qnil)
diff --git a/src/terminal.c b/src/terminal.c
index b5f244ee318..a9ecb63d85d 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -445,6 +445,8 @@ possible return values. */)
return Qpc;
case output_ns:
return Qns;
+ case output_pgtk:
+ return Qpgtk;
case output_haiku:
return Qhaiku;
default:
diff --git a/src/window.h b/src/window.h
index 2400c422c15..8e9a2eb3177 100644
--- a/src/window.h
+++ b/src/window.h
@@ -756,7 +756,7 @@ wset_next_buffers (struct window *w, Lisp_Object val)
#endif
/* True if W is a tab bar window. */
-#if defined (HAVE_WINDOW_SYSTEM)
+#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_PGTK)
# define WINDOW_TAB_BAR_P(W) \
(WINDOWP (WINDOW_XFRAME (W)->tab_bar_window) \
&& (W) == XWINDOW (WINDOW_XFRAME (W)->tab_bar_window))
diff --git a/src/xdisp.c b/src/xdisp.c
index 6dcfe3573a9..3a1bc1613f7 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -15935,7 +15935,7 @@ redisplay_internal (void)
if (!fr->glyphs_initialized_p)
return;
-#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS)
+#if defined (USE_X_TOOLKIT) || (defined (USE_GTK) && !defined (HAVE_PGTK)) || defined (HAVE_NS)
if (popup_activated ())
{
return;
@@ -25528,6 +25528,11 @@ display_menu_bar (struct window *w)
if (FRAME_W32_P (f))
return;
#endif
+#if defined (HAVE_PGTK)
+ if (FRAME_PGTK_P (f))
+ return;
+#endif
+
#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
if (FRAME_X_P (f))
return;
@@ -34011,7 +34016,7 @@ note_mouse_highlight (struct frame *f, int x, int y)
struct buffer *b;
/* When a menu is active, don't highlight because this looks odd. */
-#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) || defined (MSDOS)
+#if defined (USE_X_TOOLKIT) || (defined (USE_GTK) && !defined (HAVE_PGTK)) || defined (HAVE_NS) || defined (MSDOS)
if (popup_activated ())
return;
#endif
diff --git a/src/xfaces.c b/src/xfaces.c
index 813d89e5a3e..6f52637e916 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -247,6 +247,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#define GCGraphicsExposures 0
#endif /* HAVE_NS */
+#ifdef HAVE_PGTK
+#define GCGraphicsExposures 0
+#endif /* HAVE_PGTK */
+
#ifdef HAVE_HAIKU
#define GCGraphicsExposures 0
#endif /* HAVE_HAIKU */
@@ -579,6 +583,26 @@ x_free_gc (struct frame *f, Emacs_GC *gc)
}
#endif /* HAVE_NS */
+#ifdef HAVE_PGTK
+/* PGTK emulation of GCs */
+
+static Emacs_GC *
+x_create_gc (struct frame *f,
+ unsigned long mask,
+ Emacs_GC *xgcv)
+{
+ Emacs_GC *gc = xmalloc (sizeof *gc);
+ *gc = *xgcv;
+ return gc;
+}
+
+static void
+x_free_gc (struct frame *f, Emacs_GC *gc)
+{
+ xfree (gc);
+}
+#endif /* HAVE_NS */
+
/***********************************************************************
Frames and faces
***********************************************************************/
diff --git a/src/xsettings.c b/src/xsettings.c
index 58dfd43ad18..d6a715e1dfc 100644
--- a/src/xsettings.c
+++ b/src/xsettings.c
@@ -26,7 +26,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <byteswap.h>
#include "lisp.h"
+#ifndef HAVE_PGTK
#include "xterm.h"
+#else
+#include "gtkutil.h"
+#endif
#include "xsettings.h"
#include "frame.h"
#include "keyboard.h"
@@ -34,7 +38,12 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "termhooks.h"
#include "pdumper.h"
+#ifndef HAVE_PGTK
#include <X11/Xproto.h>
+#else
+typedef unsigned short CARD16;
+typedef unsigned int CARD32;
+#endif
#ifdef HAVE_GSETTINGS
#include <glib-object.h>
@@ -55,7 +64,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
static char *current_mono_font;
static char *current_font;
-static struct x_display_info *first_dpyinfo;
+static Display_Info *first_dpyinfo;
static Lisp_Object current_tool_bar_style;
/* Store a config changed event in to the event queue. */
@@ -73,14 +82,18 @@ store_config_changed_event (Lisp_Object arg, Lisp_Object display_name)
/* Return true if DPYINFO is still valid. */
static bool
-dpyinfo_valid (struct x_display_info *dpyinfo)
+dpyinfo_valid (Display_Info *dpyinfo)
{
bool found = false;
if (dpyinfo != NULL)
{
- struct x_display_info *d;
+ Display_Info *d;
for (d = x_display_list; !found && d; d = d->next)
+#ifndef HAVE_PGTK
found = d == dpyinfo && d->display == dpyinfo->display;
+#else
+ found = d == dpyinfo && d->gdpy == dpyinfo->gdpy;
+#endif
}
return found;
}
@@ -149,7 +162,7 @@ map_tool_bar_style (const char *tool_bar_style)
static void
store_tool_bar_style_changed (const char *newstyle,
- struct x_display_info *dpyinfo)
+ Display_Info *dpyinfo)
{
Lisp_Object style = map_tool_bar_style (newstyle);
if (EQ (current_tool_bar_style, style))
@@ -161,10 +174,12 @@ store_tool_bar_style_changed (const char *newstyle,
XCAR (dpyinfo->name_list_element));
}
+#ifndef HAVE_PGTK
#if defined USE_CAIRO || defined HAVE_XFT
#define XSETTINGS_FONT_NAME "Gtk/FontName"
#endif
#define XSETTINGS_TOOL_BAR_STYLE "Gtk/ToolbarStyle"
+#endif
enum {
SEEN_AA = 0x01,
@@ -321,10 +336,11 @@ something_changed_gconfCB (GConfClient *client,
#endif /* USE_CAIRO || HAVE_XFT */
+#ifndef HAVE_PGTK
/* Find the window that contains the XSETTINGS property values. */
static void
-get_prop_window (struct x_display_info *dpyinfo)
+get_prop_window (Display_Info *dpyinfo)
{
Display *dpy = dpyinfo->display;
@@ -339,6 +355,9 @@ get_prop_window (struct x_display_info *dpyinfo)
XUngrabServer (dpy);
}
+#endif
+
+#ifndef HAVE_PGTK
#define PAD(nr) (((nr) + 3) & ~3)
@@ -566,13 +585,15 @@ parse_settings (unsigned char *prop,
return settings_seen;
}
+#endif
+#ifndef HAVE_PGTK
/* Read settings from the XSettings property window on display for DPYINFO.
Store settings read in SETTINGS.
Return true iff successful. */
static bool
-read_settings (struct x_display_info *dpyinfo, struct xsettings *settings)
+read_settings (Display_Info *dpyinfo, struct xsettings *settings)
{
Atom act_type;
int act_form;
@@ -600,12 +621,14 @@ read_settings (struct x_display_info *dpyinfo, struct xsettings *settings)
return got_settings;
}
+#endif
+#ifndef HAVE_PGTK
/* Apply Xft settings in SETTINGS to the Xft library.
Store a Lisp event that Xft settings changed. */
static void
-apply_xft_settings (struct x_display_info *dpyinfo,
+apply_xft_settings (Display_Info *dpyinfo,
struct xsettings *settings)
{
#ifdef HAVE_XFT
@@ -731,12 +754,14 @@ apply_xft_settings (struct x_display_info *dpyinfo,
FcPatternDestroy (pat);
#endif /* HAVE_XFT */
}
+#endif
+#ifndef HAVE_PGTK
/* Read XSettings from the display for DPYINFO.
If SEND_EVENT_P store a Lisp event settings that changed. */
static void
-read_and_apply_settings (struct x_display_info *dpyinfo, bool send_event_p)
+read_and_apply_settings (Display_Info *dpyinfo, bool send_event_p)
{
struct xsettings settings;
@@ -763,11 +788,13 @@ read_and_apply_settings (struct x_display_info *dpyinfo, bool send_event_p)
}
#endif
}
+#endif
+#ifndef HAVE_PGTK
/* Check if EVENT for the display in DPYINFO is XSettings related. */
void
-xft_settings_event (struct x_display_info *dpyinfo, const XEvent *event)
+xft_settings_event (Display_Info *dpyinfo, const XEvent *event)
{
bool check_window_p = false, apply_settings_p = false;
@@ -805,6 +832,7 @@ xft_settings_event (struct x_display_info *dpyinfo, const XEvent *event)
if (apply_settings_p)
read_and_apply_settings (dpyinfo, true);
}
+#endif
/* Initialize GSettings and read startup values. */
@@ -940,10 +968,11 @@ init_gconf (void)
#endif /* HAVE_GCONF */
}
+#ifndef HAVE_PGTK
/* Init Xsettings and read startup values. */
static void
-init_xsettings (struct x_display_info *dpyinfo)
+init_xsettings (Display_Info *dpyinfo)
{
Display *dpy = dpyinfo->display;
@@ -959,13 +988,16 @@ init_xsettings (struct x_display_info *dpyinfo)
unblock_input ();
}
+#endif
void
-xsettings_initialize (struct x_display_info *dpyinfo)
+xsettings_initialize (Display_Info *dpyinfo)
{
if (first_dpyinfo == NULL) first_dpyinfo = dpyinfo;
init_gconf ();
+#ifndef HAVE_PGTK
init_xsettings (dpyinfo);
+#endif
init_gsettings ();
}
diff --git a/src/xsettings.h b/src/xsettings.h
index 26717fc08cb..dae41e8a3b8 100644
--- a/src/xsettings.h
+++ b/src/xsettings.h
@@ -20,12 +20,23 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#ifndef XSETTINGS_H
#define XSETTINGS_H
+#ifndef HAVE_PGTK
#include <X11/Xlib.h>
+#endif
struct x_display_info;
+struct pgtk_display_info;
+
+#ifndef HAVE_PGTK
+typedef struct x_display_info Display_Info;
+#else
+typedef struct pgtk_display_info Display_Info;
+#endif
-extern void xsettings_initialize (struct x_display_info *);
-extern void xft_settings_event (struct x_display_info *, const XEvent *);
+extern void xsettings_initialize (Display_Info *);
+#ifndef HAVE_PGTK
+extern void xft_settings_event (Display_Info *, const XEvent *);
+#endif
extern const char *xsettings_get_system_font (void);
#ifdef USE_LUCID
extern const char *xsettings_get_system_normal_font (void);
diff --git a/src/xwidget.c b/src/xwidget.c
index 77c1b8de39a..63ac0555dbb 100644
--- a/src/xwidget.c
+++ b/src/xwidget.c
@@ -185,8 +185,12 @@ fails. */)
# endif
xw->widgetwindow_osr = gtk_offscreen_window_new ();
+#ifndef HAVE_PGTK
gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
xw->height);
+#else
+ gtk_container_check_resize (GTK_CONTAINER (xw->widgetwindow_osr));
+#endif
if (EQ (xw->type, Qwebkit))
{