diff options
48 files changed, 3322 insertions, 579 deletions
diff --git a/Makefile.meson b/Makefile.meson index 4917fb23..40bbdb42 100644 --- a/Makefile.meson +++ b/Makefile.meson @@ -15,8 +15,7 @@ srcdir=@srcdir@ builddir=@builddir@ -vte_gtk3_api_version = @vte_gtk3_api_version@ -vte_gtk4_api_version = @vte_gtk4_api_version@ +vte_api_version = @vte_api_version@ # @@ -29,6 +28,18 @@ NINJA = ninja $(NJOBS) all: $(NINJA) +gtk3: + $(NINJA) src/app/vte-$(vte_api_version) + +gtk4: + $(NINJA) src/app/vte-$(vte_api_version)-gtk4 + +doc-gtk3: + $(NINJA) doc/reference/gtk3/meson.stamp + +doc-gtk4: + $(NINJA) doc/reference/gtk4/meson.stamp + check: MESON_TESTTHREADS=$(NTHREADS) $(NINJA) test @@ -38,8 +49,7 @@ clean: coverage: $(NINJA) coverage -doc: - $(NINJA) vte-$(vte_gtk3_api_version)-doc +doc: doc-gtk3 doc-gtk4 install: $(NINJA) install diff --git a/bindings/gir/meson.build b/bindings/gir/meson.build index 3793a430..0507e330 100644 --- a/bindings/gir/meson.build +++ b/bindings/gir/meson.build @@ -29,7 +29,7 @@ if get_option('gtk3') includes: libvte_gtk3_gir_includes, dependencies: libvte_gtk3_dep, extra_args: '-DVTE_COMPILATION', - nsversion: vte_gtk3_api_version, + nsversion: vte_api_version, namespace: 'Vte', export_packages: vte_gtk3_api_name, header: 'vte' / 'vte.h', diff --git a/bindings/glade/meson.build b/bindings/glade/meson.build index 90d4672e..6bca2aea 100644 --- a/bindings/glade/meson.build +++ b/bindings/glade/meson.build @@ -19,7 +19,7 @@ cataloguedir = gladedir / 'catalogs' pixmapdir = gladedir / 'pixmaps' catalog_conf = configuration_data() -catalog_conf.set('VTE_API_VERSION', vte_gtk3_api_version) +catalog_conf.set('VTE_API_VERSION', vte_api_version) catalog_conf.set('VERSION', vte_version) configure_file( diff --git a/doc/reference/Makefile.docs b/doc/reference/Makefile.docs new file mode 100644 index 00000000..22fe3865 --- /dev/null +++ b/doc/reference/Makefile.docs @@ -0,0 +1,511 @@ + +# +# This library 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 this library. If not, see <https://www.gnu.org/licenses/>. + +NULL = +V ?= 1 + +abs_srcdir ?= $(srcdir) +abs_builddir ?= $(builddir) +top_srcdir ?= $(abs_top_srcdir) +top_builddir ?= $(abs_top_builddir) + +datadir ?= /usr/share + +CPP = cpp +CPPFLAGS = + +GREP ?= grep +GREPFLAGS = + +LN_S = ln -s + +PACKAGE ?= vte +PACKAGE_BUGREPORT ?= https://gitlab.gnome.org/GNOME/vte/issues/ +PACKAGE_NAME ?= vte +PACKAGE_STRING ?= vte +PACKAGE_TARNAME ?= vte +PACKAGE_URL ?= https://gitlab.gnome.org/GNOME/vte/ +PACKAGE_VERSION ?= $(VERSION) + +DOC_MODULE = vte-gtk$(VTE_GTK) + +DOC_MODULE_VERSION = $(VTE_API_VERSION) + +DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.xml + +DOC_SOURCE_DIR = \ + $(top_srcdir)/src \ + $(top_srcdir)/src/vte \ + $(top_builddir)/src \ + $(top_builddir)/src/vte \ + $(NULL) + +SCANGOBJ_OPTIONS = + +SCAN_OPTIONS = \ + --deprecated-guards="VTE_DISABLE_DEPRECATED" \ + --ignore-decorators='_VTE_GNUC_NONNULL()|_VTE_PUBLIC|_VTE_DEPRECATED|_VTE_CXX_NOEXCEPT' \ + $(NULL) + +MKDB_OPTIONS = \ + --source-suffixes=c,cc,h,hh \ + --xml-mode \ + --output-format=xml \ + --name-space=vte \ + $(NULL) + +MKTMPL_OPTIONS = + +MKHTML_OPTIONS = \ + --path="$(abs_builddir)" \ + $(NULL) + +MKPDF_OPTIONS = \ + --path="$(abs_builddir)" \ + $(NULL) + +FIXXREF_OPTIONS = \ + --extra-dir=$(CAIRO_PREFIX)/share/gtk-doc/html/cairo \ + --extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html/glib \ + --extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html/gobject \ + --extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html/gio \ + --extra-dir=$(PANGO_PREFIX)/share/gtk-doc/html/pango \ + $(NULL) + +ifeq ($(VTE_GTK),3) +FIXXREF_OPTIONS += \ + --extra-dir=$(GTK_PREFIX)/share/gtk-doc/html/gdk3 \ + --extra-dir=$(GTK_PREFIX)/share/gtk-doc/html/gtk3 \ + $(NULL) +endif + +ifeq ($(VTE_GTK),4) +FIXXREF_OPTIONS += \ + --extra-dir=$(GTK_PREFIX)/share/gtk-doc/html/graphene \ + --extra-dir=$(GTK_PREFIX)/share/gtk-doc/html/gdk4 \ + --extra-dir=$(GTK_PREFIX)/share/gtk-doc/html/gsk4 \ + --extra-dir=$(GTK_PREFIX)/share/gtk-doc/html/gtk4 \ + $(NULL) +endif + +HFILE_GLOB = \ + $(top_builddir)/src/vte/*.h \ + $(top_srcdir)/src/vte/*.h \ + $(NULL) + +CFILE_GLOB = \ + $(top_builddir)/src/*.c \ + $(top_srcdir)/src/*.c \ + $(top_srcdir)/src/*.cc \ + $(NULL) + +EXTRA_HFILES = + +IGNORE_HFILES = \ + buffer.h \ + caps.hh \ + cell.hh \ + config.h \ + debug.h \ + keymap.h \ + marshal.h \ + modes.hh \ + modes-ecma.hh \ + modes-private.hh \ + parser.hh \ + parser-arg.hh \ + parser-c01.hh \ + parser-charset.hh \ + parser-charset-tables.hh \ + parser-cmd.hh \ + parser-csi.hh \ + parser-dcs.hh \ + parser-esc.hh \ + parser-glue.hh \ + parser-osc.hh \ + parser-reply.hh \ + parser-string.hh \ + ring.hh \ + tabstops.hh \ + vteconv.h \ + vtedraw.h \ + vteinternal.hh \ + vterowdata.hh \ + vtestream-base.h \ + vtestream-file.h \ + vtestream.h \ + vtetypebuiltins.h \ + vteunistr.h \ + $(NULL) + +HTML_IMAGES = + +content_files = + +expand_content_files = + +GTKDOC_CFLAGS = \ + -DVTE_COMPILATION \ + $(shell pkg-config --cflags --libs glib-2.0 gobject-2.0) \ + $(NULL) + +VTE_LIB_PATH = $(shell dirname $(VTE_LIB)) + +ifeq ($(VTE_GTK),3) +VTE_LIB_NAME = vte-$(VTE_API_VERSION) +endif +ifeq ($(VTE_GTK),4) +VTE_LIB_NAME = vte-$(VTE_API_VERSION)-gtk4 +endif + +GTKDOC_LIBS = \ + -L$(VTE_LIB_PATH) -l$(VTE_LIB_NAME) \ + $(shell pkg-config --libs --libs glib-2.0 gobject-2.0) \ + $(NULL) + +# Rules for building gtk3/4 versions of the gtk-doc inputs + +AM_V_at = $(AM_V_at_$(V)) +AM_V_at_0 = @ +AM_V_at_1 = + +AM_V_GEN = $(AM_V_GEN_$(V)) +AM_V_GEN_0 = @echo " GEN " $@; +AM_V_GEN_1 = + +vte-gtk$(VTE_GTK)-sections.txt: $(srcdir)/../vte-sections.txt.in + $(AM_V_GEN)$(CPP) -E $(CPPFLAGS) -DVTE_GTK=$(VTE_GTK) $< | $(GREP) $(GREPFLAGS) -Ev '^\s*#|^$$' > $@ + +vte-gtk$(VTE_GTK)-overrides.txt: $(srcdir)/../vte-overrides.txt.in + $(AM_V_GEN)$(CPP) -E $(CPPFLAGS) -DVTE_GTK=$(VTE_GTK) $< | $(GREP) $(GREPFLAGS) -Ev '^\s*#|^$$' > $@ || true + +vte-gtk$(VTE_GTK).types: $(srcdir)/../vte.types.in + $(AM_V_GEN)$(CPP) -E -fpreprocessed $(CPPFLAGS) -DVTE_GTK=$(VTE_GTK) $< | $(GREP) $(GREPFLAGS) -Ev '^\s*#|^$$' > $@ + +$(DOC_MAIN_SGML_FILE): $(srcdir)/../vte-docs.xml + $(AM_V_GEN)cp -f $< $@ + +# The following is copied from gtk-doc, and adapted to work with +# plain make instead of requiring automake. +# +# Copyright (C) 2003 James Henstridge +# 2004-2007 Damon Chaplin +# 2007-2017 Stefan Sauer +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +#################################### +# Everything below here is generic # +#################################### + +CC ?= cc +CFLAGS ?= + +INSTALL = install -c +INSTALL_DATA = $(INSTALL) -m 644 + +GTKDOC_CC = $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(CPPFLAGS) $(CFLAGS) +GTKDOC_LD = $(CC) $(GTKDOC_DEPS_LIBS) $(CFLAGS) $(LDFLAGS) +GTKDOC_RUN = + +GTKDOC_CHECK_PATH = gtkdoc-check +GTKDOC_REBASE = gtkdoc-rebase + +MKDIR_P ?= mkdir -p + +# We set GPATH here; this gives us semantics for GNU make +# which are more like other make's VPATH, when it comes to +# whether a source that is a target of one rule is then +# searched for in VPATH/GPATH. +# +GPATH = $(srcdir) + +HTML_DIR = $(datadir)/gtk-doc/html + +TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE) + +SETUP_FILES = \ + $(content_files) \ + $(expand_content_files) \ + $(DOC_MAIN_SGML_FILE) \ + $(DOC_MODULE)-sections.txt \ + $(DOC_MODULE)-overrides.txt + +SETUP_FILES_GENERATED = \ + $(DOC_MODULE)-sections.txt \ + $(DOC_MODULE)-overrides.txt \ + $(DOC_MODULE).types \ + $(DOC_MAIN_SGML_FILE) + +EXTRA_DIST = \ + $(HTML_IMAGES) \ + $(SETUP_FILES) + +DOC_STAMPS=setup-build.stamp scan-build.stamp sgml-build.stamp \ + html-build.stamp pdf-build.stamp \ + sgml.stamp html.stamp pdf.stamp + +SCANOBJ_FILES = \ + $(DOC_MODULE).actions \ + $(DOC_MODULE).args \ + $(DOC_MODULE).hierarchy \ + $(DOC_MODULE).interfaces \ + $(DOC_MODULE).prerequisites \ + $(DOC_MODULE).signals + +REPORT_FILES = \ + $(DOC_MODULE)-undocumented.txt \ + $(DOC_MODULE)-undeclared.txt \ + $(DOC_MODULE)-unused.txt + +gtkdoc-check.test: + $(AM_V_GEN)echo "#!/bin/sh -e" > $@; \ + echo "$(GTKDOC_CHECK_PATH) || exit 1" >> $@; \ + chmod +x $@ + +CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS) gtkdoc-check.test + +HTML_BUILD_STAMP=html-build.stamp +#PDF_BUILD_STAMP=pdf-build.stamp +PDF_BUILD_STAMP= + +all-gtk-doc: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP) +.PHONY: all-gtk-doc + +all-local: all-gtk-doc + +docs: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP) + +$(REPORT_FILES): sgml-build.stamp + +#### setup #### + +GTK_DOC_V_SETUP=$(GTK_DOC_V_SETUP_$(V)) +GTK_DOC_V_SETUP_0=@echo " DOC Preparing build"; +GTK_DOC_V_SETUP_1= + +setup-build.stamp: $(SETUP_FILES_GENERATED) + -$(GTK_DOC_V_SETUP)if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ + files=`echo $(SETUP_FILES) $(DOC_MODULE).types`; \ + if test "x$$files" != "x" ; then \ + for file in $$files ; do \ + destdir=`dirname $(abs_builddir)/$$file`; \ + test -d "$$destdir" || $(MKDIR_P) "$$destdir"; \ + test -f $(abs_srcdir)/$$file && \ + cp -pf $(abs_srcdir)/$$file $(abs_builddir)/$$file || true; \ + done; \ + fi; \ + fi + $(AM_V_at)touch setup-build.stamp + +#### scan #### + +GTK_DOC_V_SCAN=$(GTK_DOC_V_SCAN_$(V)) +GTK_DOC_V_SCAN_0=@echo " DOC Scanning header files"; +GTK_DOC_V_SCAN_1= + +GTK_DOC_V_INTROSPECT=$(GTK_DOC_V_INTROSPECT_$(V)) +GTK_DOC_V_INTROSPECT_0=@echo " DOC Introspecting gobjects"; +GTK_DOC_V_INTROSPECT_1= + +scan-build.stamp: setup-build.stamp $(HFILE_GLOB) $(CFILE_GLOB) + $(GTK_DOC_V_SCAN)_source_dir='' ; \ + for i in $(DOC_SOURCE_DIR) ; do \ + _source_dir="$${_source_dir} --source-dir=$$i" ; \ + done ; \ + gtkdoc-scan --module=$(DOC_MODULE) --ignore-headers="$(IGNORE_HFILES)" $${_source_dir} $(SCAN_OPTIONS) $(EXTRA_HFILES) + $(GTK_DOC_V_INTROSPECT)if grep -l '^..*$$' $(DOC_MODULE).types > /dev/null 2>&1 ; then \ + scanobj_options=""; \ + gtkdoc-scangobj 2>&1 --help | grep >/dev/null "\-\-verbose"; \ + if test "$$?" = "0"; then \ + if test "x$(V)" = "x1"; then \ + scanobj_options="--verbose"; \ + fi; \ + fi; \ + CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" RUN="$(GTKDOC_RUN)" CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS)" LDFLAGS="$(GTKDOC_LIBS) $(LDFLAGS)" LD_LIBRARY_PATH="$(VTE_LIB_PATH)" \ + gtkdoc-scangobj $(SCANGOBJ_OPTIONS) $$scanobj_options --module=$(DOC_MODULE); \ + else \ + for i in $(SCANOBJ_FILES) ; do \ + test -f $$i || touch $$i ; \ + done \ + fi + $(AM_V_at)touch scan-build.stamp + +$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES): scan-build.stamp + @true + +#### xml #### + +GTK_DOC_V_XML=$(GTK_DOC_V_XML_$(V)) +GTK_DOC_V_XML_0=@echo " DOC Building XML"; +GTK_DOC_V_XML_1= + +sgml-build.stamp: setup-build.stamp $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(HFILE_GLOB) $(CFILE_GLOB) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt $(expand_content_files) xml/gtkdocentities.ent $(DOC_MAIN_SGML_FILE) + $(GTK_DOC_V_XML)_source_dir='' ; \ + for i in $(DOC_SOURCE_DIR) ; do \ + _source_dir="$${_source_dir} --source-dir=$$i" ; \ + done ; \ + gtkdoc-mkdb --module=$(DOC_MODULE) --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $${_source_dir} $(MKDB_OPTIONS) + $(AM_V_at)touch sgml-build.stamp + +sgml.stamp: sgml-build.stamp + @true + +xml/gtkdocentities.ent: + $(GTK_DOC_V_XML)$(MKDIR_P) $(@D) && ( \ + echo "<!ENTITY package \"$(PACKAGE)\">"; \ + echo "<!ENTITY package_bugreport \"$(PACKAGE_BUGREPORT)\">"; \ + echo "<!ENTITY package_name \"$(PACKAGE_NAME)\">"; \ + echo "<!ENTITY package_string \"$(PACKAGE_STRING)\">"; \ + echo "<!ENTITY package_tarname \"$(PACKAGE_TARNAME)\">"; \ + echo "<!ENTITY package_url \"$(PACKAGE_URL)\">"; \ + echo "<!ENTITY package_version \"$(PACKAGE_VERSION)\">"; \ + ) > $@ + + +#### html #### + +GTK_DOC_V_HTML=$(GTK_DOC_V_HTML_$(V)) +GTK_DOC_V_HTML_0=@echo " DOC Building HTML"; +GTK_DOC_V_HTML_1= + +GTK_DOC_V_XREF=$(GTK_DOC_V_XREF_$(V)) +GTK_DOC_V_XREF_0=@echo " DOC Fixing cross-references"; +GTK_DOC_V_XREF_1= + +GTKDOC_MKHTML = gtkdoc-mkhtml + +html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(expand_content_files) + $(GTK_DOC_V_HTML)rm -rf html && mkdir html && \ + mkhtml_options=""; \ + $(GTKDOC_MKHTML) 2>&1 --help | grep >/dev/null "\-\-verbose"; \ + if test "$$?" = "0"; then \ + if test "x$(V)" = "x1"; then \ + mkhtml_options="$$mkhtml_options --verbose"; \ + fi; \ + fi; \ + $(GTKDOC_MKHTML) 2>&1 --help | grep >/dev/null "\-\-path"; \ + if test "$$?" = "0"; then \ + mkhtml_options="$$mkhtml_options --path=\"$(abs_srcdir)\""; \ + fi; \ + cd html && $(GTKDOC_MKHTML) $$mkhtml_options $(MKHTML_OPTIONS) $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE) + -@test "x$(HTML_IMAGES)" = "x" || \ + for file in $(HTML_IMAGES) ; do \ + test -f $(abs_srcdir)/$$file && cp $(abs_srcdir)/$$file $(abs_builddir)/html; \ + test -f $(abs_builddir)/$$file && cp $(abs_builddir)/$$file $(abs_builddir)/html; \ + test -f $$file && cp $$file $(abs_builddir)/html; \ + done; + $(GTK_DOC_V_XREF)gtkdoc-fixxref --module=$(DOC_MODULE) --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS) + $(AM_V_at)touch html-build.stamp + +#### pdf #### + +GTK_DOC_V_PDF=$(GTK_DOC_V_PDF_$(V)) +GTK_DOC_V_PDF_0=@echo " DOC Building PDF"; +GTK_DOC_V_PDF_1= + +pdf-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(expand_content_files) + $(GTK_DOC_V_PDF)rm -f $(DOC_MODULE).pdf && \ + mkpdf_options=""; \ + gtkdoc-mkpdf 2>&1 --help | grep >/dev/null "\-\-verbose"; \ + if test "$$?" = "0"; then \ + if test "x$(V)" = "x1"; then \ + mkpdf_options="$$mkpdf_options --verbose"; \ + fi; \ + fi; \ + if test "x$(HTML_IMAGES)" != "x"; then \ + for img in $(HTML_IMAGES); do \ + part=`dirname $$img`; \ + echo $$mkpdf_options | grep >/dev/null "\-\-imgdir=$$part "; \ + if test $$? != 0; then \ + mkpdf_options="$$mkpdf_options --imgdir=$$part"; \ + fi; \ + done; \ + fi; \ + gtkdoc-mkpdf --path="$(abs_srcdir)" $$mkpdf_options $(DOC_MODULE) $(DOC_MAIN_SGML_FILE) $(MKPDF_OPTIONS) + $(AM_V_at)touch pdf-build.stamp + +############## + +clean-local: + @rm -f *~ *.bak + @if echo $(SCAN_OPTIONS) | grep -q "\-\-rebuild-types" ; then \ + rm -f $(DOC_MODULE).types; \ + fi + @if echo $(SCAN_OPTIONS) | grep -q "\-\-rebuild-sections" ; then \ + rm -f $(DOC_MODULE)-sections.txt; \ + fi + +distclean-local: + @rm -rf xml html $(REPORT_FILES) $(DOC_MODULE).pdf \ + $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt + @if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ + rm -f $(SETUP_FILES) $(DOC_MODULE).types; \ + fi + +maintainer-clean-local: + @rm -rf xml html + +install-data-local: + @installfiles=`echo $(builddir)/html/*`; \ + if test "$$installfiles" = '$(builddir)/html/*'; \ + then echo 1>&2 'Nothing to install' ; \ + else \ + if test -n "$(DOC_MODULE_VERSION)"; then \ + installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ + else \ + installdir="$(DESTDIR)$(TARGET_DIR)"; \ + fi; \ + $(MKDIR_P) $${installdir} ; \ + for i in $$installfiles; do \ + echo ' $(INSTALL_DATA) '$$i ; \ + $(INSTALL_DATA) $$i $${installdir}; \ + done; \ + if test -n "$(DOC_MODULE_VERSION)"; then \ + mv -f $${installdir}/$(DOC_MODULE).devhelp2 \ + $${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp2; \ + fi; \ + $(GTKDOC_REBASE) --relative --dest-dir=$(DESTDIR) --html-dir=$${installdir}; \ + fi + +uninstall-local: + @if test -n "$(DOC_MODULE_VERSION)"; then \ + installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \ + else \ + installdir="$(DESTDIR)$(TARGET_DIR)"; \ + fi; \ + rm -rf $${installdir} + +dist-check-gtkdoc: docs + +dist-hook: dist-check-gtkdoc all-gtk-doc dist-hook-local + @$(MKDIR_P) $(distdir)/html + @cp ./html/* $(distdir)/html + @-cp ./$(DOC_MODULE).pdf $(distdir)/ + @-cp ./$(DOC_MODULE).types $(distdir)/ + @-cp ./$(DOC_MODULE)-sections.txt $(distdir)/ + @cd $(distdir) && rm -f $(DISTCLEANFILES) + @$(GTKDOC_REBASE) --online --relative --html-dir=$(distdir)/html + +.PHONY : dist-hook-local docs + +meson.stamp: docs + @touch meson.stamp diff --git a/doc/reference/gtk3/meson.build b/doc/reference/gtk3/meson.build new file mode 100644 index 00000000..f076098f --- /dev/null +++ b/doc/reference/gtk3/meson.build @@ -0,0 +1,55 @@ +# Copyright © 2021 Christian Persch +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This library 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <https://www.gnu.org/licenses/>. + +make_args_gtk3 = [ + '-f', meson.current_source_dir() / '..' / 'Makefile.docs', + '--directory', meson.current_build_dir(), + 'srcdir=' + meson.current_source_dir(), + 'builddir=' + meson.current_build_dir(), + 'abs_top_srcdir=' + meson.source_root(), + 'abs_top_builddir=' + meson.build_root(), + 'datadir=' + vte_prefix / vte_datadir, + 'CAIRO_PREFIX=' + cairo_dep.get_pkgconfig_variable('prefix'), + 'GLIB_PREFIX=' + glib_dep.get_pkgconfig_variable('prefix'), + 'GTK_PREFIX=' + gtk3_dep.get_pkgconfig_variable('prefix'), + 'PANGO_PREFIX=' + pango_dep.get_pkgconfig_variable('prefix'), + 'CC=' + ' '.join(cc.cmd_array()), + 'VERSION=' + meson.project_version(), + 'VTE_API_VERSION=' + vte_api_version, + 'VTE_GTK=3', + 'VTE_LIB=' + libvte_gtk3.full_path(), +] + +stamp = custom_target( + 'meson.stamp', + build_by_default: true, + capture: false, + command: [make] + make_args_gtk3 + [ + 'meson.stamp', + ], + depends: [ + libvte_gtk3, + ], + install: false, + output: 'meson.stamp', +) + +meson.add_install_script( + make, + make_args_gtk3, + 'install-data-local', +) + +# Unfortunately, there's no way to hook up the 'clean-local' target diff --git a/doc/reference/gtk4/meson.build b/doc/reference/gtk4/meson.build new file mode 100644 index 00000000..34b8bb2b --- /dev/null +++ b/doc/reference/gtk4/meson.build @@ -0,0 +1,55 @@ +# Copyright © 2021 Christian Persch +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This library 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <https://www.gnu.org/licenses/>. + +make_args_gtk4 = [ + '-f', meson.current_source_dir() / '..' / 'Makefile.docs', + '--directory', meson.current_build_dir(), + 'srcdir=' + meson.current_source_dir(), + 'builddir=' + meson.current_build_dir(), + 'abs_top_srcdir=' + meson.source_root(), + 'abs_top_builddir=' + meson.build_root(), + 'datadir=' + vte_prefix / vte_datadir, + 'CAIRO_PREFIX=' + cairo_dep.get_pkgconfig_variable('prefix'), + 'GLIB_PREFIX=' + glib_dep.get_pkgconfig_variable('prefix'), + 'GTK_PREFIX=' + gtk4_dep.get_pkgconfig_variable('prefix'), + 'PANGO_PREFIX=' + pango_dep.get_pkgconfig_variable('prefix'), + 'CC=' + ' '.join(cc.cmd_array()), + 'VERSION=' + meson.project_version(), + 'VTE_API_VERSION=' + vte_api_version, + 'VTE_GTK=4', + 'VTE_LIB=' + libvte_gtk4.full_path(), +] + +stamp = custom_target( + 'meson.stamp', + build_by_default: true, + capture: false, + command: [make] + make_args_gtk4 + [ + 'meson.stamp', + ], + depends: [ + libvte_gtk4, + ], + install: false, + output: 'meson.stamp', +) + +meson.add_install_script( + make, + make_args_gtk4, + 'install-data-local', +) + +# Unfortunately, there's no way to hook up the 'clean-local' target diff --git a/doc/reference/meson.build b/doc/reference/meson.build index b47bc7ed..b3fc3370 100644 --- a/doc/reference/meson.build +++ b/doc/reference/meson.build @@ -1,4 +1,5 @@ # Copyright © 2018, 2019 Iñigo Martínez +# Copyright © 2021 Christian Persch # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published @@ -13,96 +14,18 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see <https://www.gnu.org/licenses/>. -# Meson insufficiency! Would be so much easier to just make this -# (vte_gtk3_sources - vte_gtk3_public_headers).filter('.h'), but -# but there is no array subtraction or filtering. Or just allow listing the -# headers that we DO want to get scanned. -# So instead just list all the headers here again... :-( +# It turned out to be impossible to build gtk3 and gtk4 versions of the +# docs from the same source using meson's gnome.gtkdoc(). Instead, build +# using make with a gtk-doc.make-derived Makefile. -private_headers = [ - 'buffer.h', - 'caps.hh', - 'cell.hh', - 'config.h', - 'debug.h', - 'keymap.h', - 'marshal.h', - 'modes.hh', - 'modes-ecma.hh', - 'modes-private.hh', - 'parser.hh', - 'parser-arg.hh', - 'parser-c01.hh', - 'parser-charset.hh', - 'parser-charset-tables.hh', - 'parser-cmd.hh', - 'parser-csi.hh', - 'parser-dcs.hh', - 'parser-esc.hh', - 'parser-glue.hh', - 'parser-osc.hh', - 'parser-reply.hh', - 'parser-string.hh', - 'ring.hh', - 'tabstops.hh', - 'vteconv.h', - 'vtedraw.h', - 'vteinternal.hh', - 'vterowdata.hh', - 'vtestream-base.h', - 'vtestream-file.h', - 'vtestream.h', - 'vtetypebuiltins.h', - 'vteunistr.h', -] +make = find_program('gmake', 'make') -scan_args = [ - '--deprecated-guards="VTE_DISABLE_DEPRECATED"', - '--ignore-decorators=_VTE_GNUC_NONNULL\s*\([^)]*\)|_VTE_CXX_NOEXCEPT', -] - -glib_prefix = glib_dep.get_pkgconfig_variable('prefix') - -version_conf = configuration_data() -version_conf.set('VERSION', vte_version) - -content_files = configure_file( - input: 'version.xml.in', - output: '@BASENAME@', - configuration: version_conf -) +cairo_dep = dependency('cairo') if get_option('gtk3') - gtk3_prefix = gtk3_dep.get_pkgconfig_variable('prefix') - - fixxref_args = [ - '--html-dir=' + (vte_prefix / gnome.gtkdoc_html_dir(vte_gtk3_api_name)), - '--extra-dir=' + (glib_prefix / gnome.gtkdoc_html_dir('glib')), - '--extra-dir=' + (glib_prefix / gnome.gtkdoc_html_dir('gio')), - '--extra-dir=' + (gtk3_prefix / gnome.gtkdoc_html_dir('gdk')), - '--extra-dir=' + (gtk3_prefix / gnome.gtkdoc_html_dir('gdk-pixbuf')), - '--extra-dir=' + (gtk3_prefix / gnome.gtkdoc_html_dir('gtk')), - ] - - gnome.gtkdoc( - 'vte', - main_xml: 'vte-docs.xml', - module_version: vte_api_version, - src_dir: [src_inc, vte_inc], - ignore_headers: private_headers, - include_directories: top_inc, - dependencies: libvte_gtk3_dep, - c_args: '-DVTE_COMPILATION', - namespace: 'vte', - scan_args: scan_args, - mkdb_args: '--source-suffixes=h,hh,c,cc', - fixxref_args: fixxref_args, - gobject_typesfile: 'vte.types', - content_files: content_files, - install: true, - ) + subdir('gtk3') endif if get_option('gtk4') - assert(false, 'not yet supported') + subdir('gtk4') endif diff --git a/doc/reference/version.xml.in b/doc/reference/version.xml.in deleted file mode 100644 index d78bda93..00000000 --- a/doc/reference/version.xml.in +++ /dev/null @@ -1 +0,0 @@ -@VERSION@ diff --git a/doc/reference/vte-docs.xml b/doc/reference/vte-docs.xml index e3cd5180..76296673 100644 --- a/doc/reference/vte-docs.xml +++ b/doc/reference/vte-docs.xml @@ -1,7 +1,9 @@ <?xml version="1.0"?> <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" - "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ -<!ENTITY version SYSTEM "version.xml"> + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" +[ + <!ENTITY % gtkdocentities SYSTEM "xml/gtkdocentities.ent"> + %gtkdocentities; ]> <book id="index" xmlns:xi="http://www.w3.org/2003/XInclude"> <!-- @@ -24,7 +26,7 @@ <bookinfo> <title>VTE Reference Manual</title> <releaseinfo> - Documentation for VTE version &version;. + Documentation for VTE version &package_version;. The latest version of this documentation can be found on-line at the <ulink role="online-location" url="http://library.gnome.org/devel/vte/">GNOME Library</ulink>. </releaseinfo> @@ -133,8 +135,12 @@ <title>Index of new symbols in 0.64</title> <xi:include href="xml/api-index-0.64.xml"><xi:fallback /></xi:include> </index> + <index id="api-index-0-66" role="0.66"> + <title>Index of new symbols in 0.66</title> + <xi:include href="xml/api-index-0.66.xml"><xi:fallback /></xi:include> + </index> - <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> + <xi:include href="xml/annotation-glossary.xml"></xi:include> <appendix id="licence"> <title>Licence</title> diff --git a/doc/reference/vte-overrides.txt b/doc/reference/vte-overrides.txt.in index e69de29b..e69de29b 100644 --- a/doc/reference/vte-overrides.txt +++ b/doc/reference/vte-overrides.txt.in diff --git a/doc/reference/vte-sections.txt b/doc/reference/vte-sections.txt.in index c69181da..84afe8c3 100644 --- a/doc/reference/vte-sections.txt +++ b/doc/reference/vte-sections.txt.in @@ -71,12 +71,16 @@ vte_terminal_reset vte_terminal_get_text vte_terminal_get_text_range vte_terminal_get_cursor_position +#if VTE_GTK == 3 vte_terminal_hyperlink_check_event +#endif vte_terminal_match_add_regex vte_terminal_match_remove vte_terminal_match_remove_all vte_terminal_match_check +#if VTE_GTK == 3 vte_terminal_match_check_event +#endif vte_terminal_match_set_cursor_name vte_terminal_set_cjk_ambiguous_width vte_terminal_get_cjk_ambiguous_width @@ -93,8 +97,10 @@ vte_terminal_search_get_regex vte_terminal_search_get_wrap_around vte_terminal_search_set_regex vte_terminal_search_set_wrap_around +#if VTE_GTK == 3 vte_terminal_event_check_regex_array vte_terminal_event_check_regex_simple +#endif /* VTE_GTK */ <SUBSECTION> VteFeatureFlags @@ -113,9 +119,11 @@ vte_terminal_set_pty vte_terminal_pty_new_sync vte_terminal_watch_child +#if VTE_GTK == 3 <SUBSECTION> vte_terminal_set_clear_background vte_terminal_get_color_background_for_draw +#endif /* VTE_GTK == 3 */ <SUBSECTION Standard> VTE_TYPE_CURSOR_BLINK_MODE @@ -150,14 +158,18 @@ vte_terminal_get_current_file_uri <SUBSECTION Deprecated> vte_terminal_copy_clipboard vte_terminal_match_set_cursor +#if VTE_GTK == 3 vte_terminal_match_set_cursor_type vte_terminal_match_add_gregex vte_terminal_search_get_gregex vte_terminal_search_set_gregex vte_terminal_event_check_gregex_simple +#endif /* VTE_GTK == 3 */ vte_terminal_spawn_sync +#if VTE_GTK == 3 vte_terminal_get_geometry_hints vte_terminal_set_geometry_hints_for_window +#endif /* VTE_GTK == 3 */ vte_terminal_get_icon_title vte_terminal_set_encoding vte_terminal_get_encoding diff --git a/doc/reference/vte.types b/doc/reference/vte.types.in index d7db2a09..d7db2a09 100644 --- a/doc/reference/vte.types +++ b/doc/reference/vte.types.in diff --git a/meson.build b/meson.build index 5245090a..070d43a4 100644 --- a/meson.build +++ b/meson.build @@ -34,7 +34,10 @@ project( gtk3_req_version = '3.20.0' gtk3_min_req_version = '3.18' gtk3_max_allowed_version = '3.20' -gtk4_req_version = '4.0.0' + +gtk4_req_version = '4.0.1' +gtk4_min_req_version = '4.0' +gtk4_max_allowed_version = '4.0' fribidi_req_version = '1.0.0' gio_req_version = '2.52.0' @@ -54,11 +57,8 @@ vte_api_minor_version = 91 vte_api_version = '@0@.@1@'.format(vte_api_major_version, vte_api_minor_version) vte_api_name = 'vte-@0@.@1@'.format(vte_api_major_version, vte_api_minor_version) -vte_gtk3_api_version = '@0@.@1@'.format(vte_api_major_version, vte_api_minor_version) -vte_gtk4_api_version = '@0@.@1@'.format(vte_api_major_version + 1, vte_api_minor_version) - -vte_gtk3_api_name = 'vte-' + vte_gtk3_api_version -vte_gtk4_api_name = 'vte-' + vte_gtk4_api_version +vte_gtk3_api_name = 'vte-' + vte_api_version +vte_gtk4_api_name = 'vte-' + vte_api_version + '-gtk4' vte_gtk3_api_path = vte_gtk3_api_name / 'vte' vte_gtk4_api_path = vte_gtk4_api_name / 'vte' @@ -141,6 +141,17 @@ if get_option('gtk3') gtk3_version_cppflags += '-DGDK_VERSION_MAX_ALLOWED=(G_ENCODE_VERSION(' + ver[0] + ',' + ver[1] + '))' endif +if get_option('gtk4') + gtk4_version_cppflags = [] + + ver = gtk4_min_req_version.split('.') + gtk4_version_cppflags += '-DGDK_VERSION_MIN_REQUIRED=(G_ENCODE_VERSION(' + ver[0] + ',' + ver[1] + '))' + + ver = gtk4_max_allowed_version.split('.') + gtk4_version_cppflags += '-DGDK_VERSION_MAX_ALLOWED=(G_ENCODE_VERSION(' + ver[0] + ',' + ver[1] + '))' +endif + + # FIXME AC_USE_SYSTEM_EXTENSIONS also supported non-gnu systems config_h.set10('_GNU_SOURCE', true) @@ -427,7 +438,7 @@ else endif if get_option('gtk4') - gtk4_dep = dependency('gtk+-4.0', version: '>=' + gtk4_req_version) + gtk4_dep = dependency('gtk4', version: '>=' + gtk4_req_version) else gtk4_dep = dependency('', required: false) endif @@ -462,6 +473,10 @@ subdir('bindings') subdir('po') if get_option('docs') + assert(meson.version().version_compare('>= 0.55.0'), + 'meson >= 0.55 is required to build docs' + ) + subdir('doc/reference') endif @@ -470,8 +485,7 @@ endif makefile_conf = configuration_data() makefile_conf.set('srcdir', meson.current_source_dir()) makefile_conf.set('builddir', meson.current_build_dir()) -makefile_conf.set('vte_gtk3_api_version', vte_gtk3_api_version) -makefile_conf.set('vte_gtk4_api_version', vte_gtk4_api_version) +makefile_conf.set('vte_api_version', vte_api_version) configure_file( input: 'Makefile.meson', @@ -516,4 +530,8 @@ output += '\n' output += ' Prefix: ' + get_option('prefix') + '\n' message(output) +if get_option('gtk4') + warning('GTK+ 4.0 support is experimental; API and ABI are subject to change without notice\n') +endif + # Done diff --git a/po/POTFILES.skip b/po/POTFILES.skip index fffa7710..13d510de 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -3,8 +3,8 @@ bindings/vala/search-popover.ui src/vtespawn.cc src/app/app.cc src/app/appmenu-gtk3.ui -src/app/appmenu.ui +src/app/appmenu-gtk4.ui src/app/search-popover-gtk3.ui -src/app/search-popover.ui +src/app/search-popover-gtk4.ui src/app/window-gtk3.ui -src/app/window.ui +src/app/window-gtk4.ui diff --git a/src/app/app-gtk4.gresource.xml b/src/app/app-gtk4.gresource.xml new file mode 100644 index 00000000..6c76b21f --- /dev/null +++ b/src/app/app-gtk4.gresource.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright © 2014, 2020 Christian Persch + + This program 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. + + This program 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 this program. If not, see <https://www.gnu.org/licenses/>. +--> +<gresources> + <gresource prefix="/org/gnome/vte/app"> + <file alias="ui/search-popover.ui" compressed="true" preprocess="xml-stripblanks">search-popover-gtk4.ui</file> + <file alias="ui/window.ui" compressed="true" preprocess="xml-stripblanks">window-gtk4.ui</file> + <file alias="gtk/menus.ui" compressed="true" preprocess="xml-stripblanks">appmenu-gtk4.ui</file> + </gresource> +</gresources> diff --git a/src/app/app.cc b/src/app/app.cc index 01975287..4c83c207 100644 --- a/src/app/app.cc +++ b/src/app/app.cc @@ -38,6 +38,7 @@ #include <vector> #include "std-glue.hh" +#include "cairo-glue.hh" #include "glib-glue.hh" #include "libc-glue.hh" #include "pango-glue.hh" @@ -61,13 +62,11 @@ public: gboolean feed_stdin{false}; gboolean icon_title{false}; gboolean keep{false}; - gboolean no_argb_visual{false}; gboolean no_bidi{false}; gboolean no_bold{false}; gboolean no_builtin_dingus{false}; gboolean no_context_menu{false}; gboolean no_decorations{false}; - gboolean no_double_buffer{false}; gboolean no_fallback_scrolling{false}; gboolean no_geometry_hints{false}; gboolean no_hyperlink{false}; @@ -121,6 +120,11 @@ public: VteTextBlinkMode text_blink_mode{VTE_TEXT_BLINK_ALWAYS}; vte::glib::RefPtr<GtkCssProvider> css{}; +#if VTE_GTK == 3 + gboolean no_argb_visual{false}; + gboolean no_double_buffer{false}; +#endif /* VTE_GTK == 3 */ + ~Options() { g_clear_object(&background_pixbuf); g_free(command); @@ -371,8 +375,12 @@ private: Options* that = static_cast<Options*>(data); auto css = vte::glib::take_ref(gtk_css_provider_new()); +#if VTE_GTK == 3 if (!gtk_css_provider_load_from_path(css.get(), value, error)) return false; +#elif VTE_GTK == 4 + gtk_css_provider_load_from_path(css.get(), value); +#endif /* VTE_GKT */ that->css = std::move(css); return true; @@ -550,8 +558,6 @@ public: "Enable the setting of the icon title", nullptr }, { "keep", 'k', 0, G_OPTION_ARG_NONE, &keep, "Live on after the command exits", nullptr }, - { "no-argb-visual", 0, 0, G_OPTION_ARG_NONE, &no_argb_visual, - "Don't use an ARGB visual", nullptr }, { "no-bidi", 0, 0, G_OPTION_ARG_NONE, &no_bidi, "Disable BiDi", nullptr }, { "no-bold", 0, 0, G_OPTION_ARG_NONE, &no_bold, @@ -562,8 +568,6 @@ public: "Disable context menu", nullptr }, { "no-decorations", 0, 0, G_OPTION_ARG_NONE, &no_decorations, "Disable window decorations", nullptr }, - { "no-double-buffer", '2', 0, G_OPTION_ARG_NONE, &no_double_buffer, - "Disable double-buffering", nullptr }, { "no-fallback-scrolling", 0, 0, G_OPTION_ARG_NONE, &no_fallback_scrolling, "Disable fallback scrolling", nullptr }, { "no-geometry-hints", 'G', 0, G_OPTION_ARG_NONE, &no_geometry_hints, @@ -617,8 +621,6 @@ public: nullptr, nullptr }, { "console", 'C', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &console, nullptr, nullptr }, - { "double-buffer", '2', G_OPTION_FLAG_REVERSE | G_OPTION_FLAG_HIDDEN, - G_OPTION_ARG_NONE, &no_double_buffer, nullptr, nullptr }, { "pty-flags", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &dummy_string, nullptr, nullptr }, { "scrollbar-policy", 'P', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, @@ -633,6 +635,15 @@ public: #endif { "use-theme-colors", 0, 0, G_OPTION_ARG_NONE, &use_theme_colors, "Use foreground and background colors from the gtk+ theme", nullptr }, + +#if VTE_GTK == 3 + { "no-argb-visual", 0, 0, G_OPTION_ARG_NONE, &no_argb_visual, + "Don't use an ARGB visual", nullptr }, + { "double-buffer", '2', G_OPTION_FLAG_REVERSE | G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_NONE, &no_double_buffer, nullptr, nullptr }, + { "no-double-buffer", '2', 0, G_OPTION_ARG_NONE, &no_double_buffer, + "Disable double-buffering", nullptr }, +#endif /* VTE_GTK == 3 */ { nullptr } }; @@ -666,7 +677,9 @@ public: g_option_group_add_entries(group, entries); g_option_context_set_main_group(context.get(), group); +#if VTE_GTK == 3 g_option_context_add_group(context.get(), gtk_get_option_group(true)); +#endif bool rv = g_option_context_parse(context.get(), &argc, &argv, error); @@ -677,6 +690,14 @@ public: swap(fg_color, bg_color); } +#if VTE_GTK == 4 + if (rv && !gtk_init_check()) { + g_set_error_literal(error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + "Failed to initialise gtk+"); + rv = false; + } +#endif /* VTE_GTK == 4 */ + return rv; } }; @@ -808,7 +829,11 @@ vteapp_search_popover_update_sensitivity(VteappSearchPopover* popover) static void vteapp_search_popover_update_regex(VteappSearchPopover* popover) { - char const* search_text = gtk_entry_get_text(GTK_ENTRY(popover->search_entry)); +#if VTE_GTK == 3 + auto search_text = gtk_entry_get_text(GTK_ENTRY(popover->search_entry)); +#elif VTE_GTK == 4 + auto search_text = gtk_editable_get_text(GTK_EDITABLE(popover->search_entry)); +#endif /* VTE_GTK */ bool caseless = gtk_toggle_button_get_active(popover->match_case_checkbutton) == FALSE; char* pattern; @@ -971,10 +996,18 @@ static GtkWidget* vteapp_search_popover_new(VteTerminal* terminal, GtkWidget* relative_to) { - return reinterpret_cast<GtkWidget*>(g_object_new(VTEAPP_TYPE_SEARCH_POPOVER, - "terminal", terminal, - "relative-to", relative_to, - nullptr)); + auto popover = reinterpret_cast<GtkWidget*>(g_object_new(VTEAPP_TYPE_SEARCH_POPOVER, + "terminal", terminal, +#if VTE_GTK == 3 + "relative-to", relative_to, +#endif + nullptr)); + +#if VTE_GTK == 4 + gtk_widget_set_parent(popover, relative_to); +#endif + + return popover; } /* terminal */ @@ -992,7 +1025,11 @@ typedef struct _VteappTerminalClass VteappTerminalClass; struct _VteappTerminal { VteTerminal parent; + //#if VTE_GTK == 3 cairo_pattern_t* background_pattern; + //#elif VTE_GTK == 4 + // GdkTexture* background_texture; + //#endif bool has_backdrop; bool use_backdrop; }; @@ -1012,35 +1049,58 @@ vteapp_terminal_realize(GtkWidget* widget) { GTK_WIDGET_CLASS(vteapp_terminal_parent_class)->realize(widget); - VteappTerminal* terminal = VTEAPP_TERMINAL(widget); - if (options.background_pixbuf != nullptr) { - auto surface = gdk_cairo_surface_create_from_pixbuf(options.background_pixbuf, - 0 /* take scale from window */, - gtk_widget_get_window(widget)); - terminal->background_pattern = cairo_pattern_create_for_surface(surface); - cairo_surface_destroy(surface); + if (!options.background_pixbuf) + return; - cairo_pattern_set_extend(terminal->background_pattern, options.background_extend); - } + auto terminal = VTEAPP_TERMINAL(widget); + +#if VTE_GTK == 3 + auto surface = vte::take_freeable + (gdk_cairo_surface_create_from_pixbuf(options.background_pixbuf, + 0 /* take scale from window */, + gtk_widget_get_window(widget))); +#elif VTE_GTK == 4 + auto const width = gdk_pixbuf_get_width(options.background_pixbuf); + auto const height = gdk_pixbuf_get_height(options.background_pixbuf); + auto surface = vte::take_freeable(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + width, height)); + auto cr = vte::take_freeable(cairo_create(surface.get())); + gdk_cairo_set_source_pixbuf(cr.get(), options.background_pixbuf, 0, 0); + cairo_paint(cr.get()); + cairo_surface_flush(surface.get()); // FIXME necessary? +#endif + terminal->background_pattern = cairo_pattern_create_for_surface(surface.get()); + + cairo_pattern_set_extend(terminal->background_pattern, options.background_extend); + + + //#elif VTE_GTK == 4 + // terminal->background_texture = gdk_texture_new_for_pixbuf(options.background_pixbuf); + //#endif /* VTE_GTK */ } static void vteapp_terminal_unrealize(GtkWidget* widget) { - VteappTerminal* terminal = VTEAPP_TERMINAL(widget); +#if VTE_GTK == 3 + auto terminal = VTEAPP_TERMINAL(widget); + if (terminal->background_pattern != nullptr) { cairo_pattern_destroy(terminal->background_pattern); terminal->background_pattern = nullptr; } +#endif /* VTE_GTK */ GTK_WIDGET_CLASS(vteapp_terminal_parent_class)->unrealize(widget); } -static gboolean -vteapp_terminal_draw(GtkWidget* widget, - cairo_t* cr) +static void +vteapp_terminal_draw_background(GtkWidget* widget, + cairo_t* cr) { - VteappTerminal* terminal = VTEAPP_TERMINAL(widget); +#if VTE_GTK == 3 + auto terminal = VTEAPP_TERMINAL(widget); + if (terminal->background_pattern != nullptr) { cairo_push_group(cr); @@ -1063,8 +1123,29 @@ vteapp_terminal_draw(GtkWidget* widget, cairo_paint_with_alpha(cr, options.get_alpha_bg_for_draw()); } +#endif /* VTE_GTK == 3 */ +} + +#if VTE_GTK == 4 - auto rv = GTK_WIDGET_CLASS(vteapp_terminal_parent_class)->draw(widget, cr); +static void +vteapp_terminal_draw_background(GtkWidget* widget, + GtkSnapshot* snapshot) +{ + auto grect = GRAPHENE_RECT_INIT(float(0), float(0), + float(gtk_widget_get_allocated_width(widget)), + float(gtk_widget_get_allocated_height(widget))); + auto cr = vte::take_freeable(gtk_snapshot_append_cairo(snapshot, &grect)); + vteapp_terminal_draw_background(widget, cr.get()); +} + +#endif /* VTE_GTK == 4 */ + +static void +vteapp_terminal_draw_backdrop(GtkWidget* widget, + cairo_t* cr) +{ + auto terminal = VTEAPP_TERMINAL(widget); if (terminal->use_backdrop && terminal->has_backdrop) { cairo_set_operator(cr, CAIRO_OPERATOR_OVER); @@ -1074,49 +1155,162 @@ vteapp_terminal_draw(GtkWidget* widget, gtk_widget_get_allocated_height(widget)); cairo_paint(cr); } +} + +#if VTE_GTK == 4 + +static void +vteapp_terminal_draw_backdrop(GtkWidget* widget, + GtkSnapshot* snapshot) +{ + auto grect = GRAPHENE_RECT_INIT(float(0), float(0), + float(gtk_widget_get_allocated_width(widget)), + float(gtk_widget_get_allocated_height(widget))); + auto cr = vte::take_freeable(gtk_snapshot_append_cairo(snapshot, &grect)); + vteapp_terminal_draw_backdrop(widget, cr.get()); +} + +#endif /* VTE_GTK == 4 */ + +#if VTE_GTK == 3 + +static gboolean +vteapp_terminal_draw(GtkWidget* widget, + cairo_t* cr) +{ + vteapp_terminal_draw_background(widget, cr); + + auto const rv = GTK_WIDGET_CLASS(vteapp_terminal_parent_class)->draw(widget, cr); + + vteapp_terminal_draw_backdrop(widget, cr); return rv; } -static auto dti(double d) -> unsigned { return CLAMP((d*255), 0, 255); } +#endif /* VTE_GTK == 3 */ + +static void +vteapp_terminal_update_theme_colors(GtkWidget* widget) +{ + if (!options.use_theme_colors) + return; + + auto terminal = VTEAPP_TERMINAL(widget); + auto context = gtk_widget_get_style_context(widget); + +#if VTE_GTK == 3 + auto const flags = gtk_style_context_get_state(context); +#endif + + auto theme_fg = GdkRGBA{}; + gtk_style_context_get_color(context, +#if VTE_GTK == 3 + flags, +#endif + &theme_fg); + + auto theme_bg = GdkRGBA{}; +#if VTE_GTK == 3 + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + gtk_style_context_get_background_color(context, flags, &theme_bg); + G_GNUC_END_IGNORE_DEPRECATIONS; +#elif VTE_GTK == 4 + // FIXMEgtk4 "background-color" lookup always fails + if (!gtk_style_context_lookup_color(context, "text_view_bg", &theme_bg)) { + verbose_print("Failed to get theme background color\n"); + return; + } +#endif + + auto dti = [](double d) -> unsigned { return std::clamp(unsigned(d*255), 0u, 255u); }; + + verbose_print("Theme colors: foreground is #%02X%02X%02X, background is #%02X%02X%02X\n", + dti(theme_fg.red), dti(theme_fg.green), dti(theme_fg.blue), + dti(theme_bg.red), dti(theme_bg.green), dti(theme_bg.blue)); + + theme_fg.alpha = 1.; + theme_bg.alpha = options.get_alpha_bg(); + vte_terminal_set_colors(VTE_TERMINAL(terminal), &theme_fg, &theme_bg, nullptr, 0); +} + +#if VTE_GTK == 3 static void vteapp_terminal_style_updated(GtkWidget* widget) { GTK_WIDGET_CLASS(vteapp_terminal_parent_class)->style_updated(widget); - auto context = gtk_widget_get_style_context(widget); - auto flags = gtk_style_context_get_state(context); + auto terminal = VTEAPP_TERMINAL(widget); - VteappTerminal* terminal = VTEAPP_TERMINAL(widget); + auto context = gtk_widget_get_style_context(widget); + auto const flags = gtk_style_context_get_state(context); terminal->has_backdrop = (flags & GTK_STATE_FLAG_BACKDROP) != 0; - if (options.use_theme_colors) { - auto theme_fg = GdkRGBA{}; - gtk_style_context_get_color(context, flags, &theme_fg); - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - auto theme_bg = GdkRGBA{}; - gtk_style_context_get_background_color(context, flags, &theme_bg); - G_GNUC_END_IGNORE_DEPRECATIONS; + vteapp_terminal_update_theme_colors(widget); +} - verbose_print("Theme colors: foreground is #%02X%02X%02X, background is #%02X%02X%02X\n", - dti(theme_fg.red), dti(theme_fg.green), dti(theme_fg.blue), - dti(theme_bg.red), dti(theme_bg.green), dti(theme_bg.blue)); +#endif /* VTE_GTK == 3 */ - theme_fg.alpha = 1.; - theme_bg.alpha = options.get_alpha_bg(); - vte_terminal_set_colors(VTE_TERMINAL(terminal), &theme_fg, &theme_bg, nullptr, 0); - } +#if VTE_GTK == 4 + +static void +vteapp_terminal_snapshot(GtkWidget* widget, + GtkSnapshot* snapshot_object) +{ + vteapp_terminal_draw_background(widget, snapshot_object); + + GTK_WIDGET_CLASS(vteapp_terminal_parent_class)->snapshot(widget, snapshot_object); + + vteapp_terminal_draw_backdrop(widget, snapshot_object); } static void +vteapp_terminal_css_changed(GtkWidget* widget, + GtkCssStyleChange* change) +{ + GTK_WIDGET_CLASS(vteapp_terminal_parent_class)->css_changed(widget, change); + + vteapp_terminal_update_theme_colors(widget); +} + +static void +vteapp_terminal_state_flags_changed(GtkWidget* widget, + GtkStateFlags old_flags) +{ + GTK_WIDGET_CLASS(vteapp_terminal_parent_class)->state_flags_changed(widget, old_flags); + + auto terminal = VTEAPP_TERMINAL(widget); + auto const flags = gtk_widget_get_state_flags(widget); + terminal->has_backdrop = (flags & GTK_STATE_FLAG_BACKDROP) != 0; +} + +static void +vteapp_terminal_system_setting_changed(GtkWidget* widget, + GtkSystemSetting setting) +{ + GTK_WIDGET_CLASS(vteapp_terminal_parent_class)->system_setting_changed(widget, setting); + + // FIXMEgtk4 find a way to update colours on theme change like gtk3 above +} + +#endif /* VTE_GTK == 4 */ + +static void vteapp_terminal_class_init(VteappTerminalClass *klass) { - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + auto widget_class = GTK_WIDGET_CLASS(klass); widget_class->realize = vteapp_terminal_realize; widget_class->unrealize = vteapp_terminal_unrealize; + +#if VTE_GTK == 3 widget_class->draw = vteapp_terminal_draw; widget_class->style_updated = vteapp_terminal_style_updated; +#elif VTE_GTK == 4 + widget_class->snapshot = vteapp_terminal_snapshot; + widget_class->css_changed = vteapp_terminal_css_changed; + widget_class->state_flags_changed = vteapp_terminal_state_flags_changed; + widget_class->system_setting_changed = vteapp_terminal_system_setting_changed; +#endif gtk_widget_class_set_css_name(widget_class, "vteapp-terminal"); } @@ -1128,8 +1322,10 @@ vteapp_terminal_init(VteappTerminal *terminal) terminal->has_backdrop = false; terminal->use_backdrop = options.backdrop; +#if VTE_GTK == 3 if (options.background_pixbuf != nullptr) vte_terminal_set_clear_background(VTE_TERMINAL(terminal), false); +#endif /* VTE_GTK == 3 */ } static GtkWidget * @@ -1165,12 +1361,9 @@ struct _VteappWindow { /* end */ VteTerminal* terminal; - GtkClipboard* clipboard; GPid child_pid; GtkWidget* search_popover; - bool fullscreen{false}; - /* used for updating the geometry hints */ int cached_cell_width{0}; int cached_cell_height{0}; @@ -1178,6 +1371,15 @@ struct _VteappWindow { int cached_chrome_height{0}; int cached_csd_width{0}; int cached_csd_height{0}; + +#if VTE_GTK == 3 + GtkClipboard* clipboard; + GdkWindowState window_state{GdkWindowState(0)}; +#endif +#if VTE_GTK == 4 + GdkClipboard* clipboard; + GdkToplevelState toplevel_state{GdkToplevelState(0)}; +#endif }; struct _VteappWindowClass { @@ -1221,6 +1423,7 @@ vteapp_window_add_dingus(VteappWindow* window, static void vteapp_window_update_geometry(VteappWindow* window) { +#if VTE_GTK == 3 GtkWidget* window_widget = GTK_WIDGET(window); GtkWidget* terminal_widget = GTK_WIDGET(window->terminal); @@ -1301,18 +1504,43 @@ vteapp_window_update_geometry(VteappWindow* window) window->cached_cell_width, window->cached_cell_height, window->cached_chrome_width, window->cached_chrome_height, window->cached_csd_width, window->cached_csd_height); +#elif VTE_GTK == 4 + // FIXMEgtk4 there appears to be no way to do this with gtk4 ? maybe go to X/wayland + // directly to set the geometry hints? +#endif } +#include <gdk/gdk.h> + static void vteapp_window_resize(VteappWindow* window) { - /* Don't do this for maximised or tiled windows. */ - auto win = gtk_widget_get_window(GTK_WIDGET(window)); - if (win != nullptr && - (gdk_window_get_state(win) & (GDK_WINDOW_STATE_MAXIMIZED | - GDK_WINDOW_STATE_FULLSCREEN | - GDK_WINDOW_STATE_TILED)) != 0) + /* Don't do this for fullscreened, maximised, or tiled windows. */ +#if VTE_GTK == 3 + if (window->window_state & (GDK_WINDOW_STATE_MAXIMIZED | + GDK_WINDOW_STATE_FULLSCREEN | + GDK_WINDOW_STATE_TILED | +#if GTK_CHECK_VERSION(3,22,23) + GDK_WINDOW_STATE_TOP_TILED | + GDK_WINDOW_STATE_BOTTOM_TILED | + GDK_WINDOW_STATE_LEFT_TILED | + GDK_WINDOW_STATE_RIGHT_TILED | +#endif + 0)) + return; +#elif VTE_GTK == 4 + if (window->toplevel_state & (GDK_TOPLEVEL_STATE_MAXIMIZED | + GDK_TOPLEVEL_STATE_FULLSCREEN | + GDK_TOPLEVEL_STATE_TILED | + GDK_TOPLEVEL_STATE_TOP_TILED | + GDK_TOPLEVEL_STATE_BOTTOM_TILED | + GDK_TOPLEVEL_STATE_LEFT_TILED | + GDK_TOPLEVEL_STATE_RIGHT_TILED)) return; +#endif /* VTE_GTK */ + +#if VTE_GTK == 3 + // FIXMEgtk4 /* First, update the geometry hints, so that the cached_* members are up-to-date */ vteapp_window_update_geometry(window); @@ -1327,11 +1555,13 @@ vteapp_window_resize(VteappWindow* window) columns, rows, pixel_width, pixel_height); gtk_window_resize(GTK_WINDOW(window), pixel_width, pixel_height); +#endif /* VTE_GTK == 3 FIXMEgtk4 */ } static void vteapp_window_parse_geometry(VteappWindow* window) { +#if VTE_GTK == 3 /* First update the geometry hints, so that gtk_window_parse_geometry() * knows the char width/height and base size increments. */ @@ -1376,6 +1606,9 @@ vteapp_window_parse_geometry(VteappWindow* window) vteapp_window_resize(window); } } +#elif VTE_GTK == 4 + // FIXMEgtk4 ???? +#endif /* VTE_GTK */ } static void @@ -1409,8 +1642,13 @@ window_spawn_cb(VteTerminal* terminal, auto msg = vte::glib::take_string(g_strdup_printf("Spawning failed: %s", error->message)); if (options.keep) vte_terminal_feed(window->terminal, msg.get(), -1); - else + else { +#if VTE_GTK == 3 gtk_widget_destroy(GTK_WIDGET(window)); +#elif VTE_GTK == 4 + gtk_window_destroy(GTK_WINDOW(window)); +#endif + } } } @@ -1568,16 +1806,34 @@ window_update_copy_sensitivity(VteappWindow* window) } static void +window_update_fullscreen_state(VteappWindow* window) +{ +#if VTE_GTK == 3 + auto const fullscreen = (window->window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0; +#elif VTE_GTK == 4 + auto const fullscreen = (window->toplevel_state & GDK_TOPLEVEL_STATE_FULLSCREEN) != 0; +#endif + auto action = g_action_map_lookup_action(G_ACTION_MAP(window), "fullscreen"); + g_simple_action_set_state(G_SIMPLE_ACTION(action), g_variant_new_boolean (fullscreen)); +} + +static void window_update_paste_sensitivity(VteappWindow* window) { + bool can_paste = false; + +#if VTE_GTK == 3 GdkAtom* targets; int n_targets; - bool can_paste = false; if (gtk_clipboard_wait_for_targets(window->clipboard, &targets, &n_targets)) { can_paste = gtk_targets_include_text(targets, n_targets); g_free(targets); } +#elif VTE_GTK == 4 + auto formats = gdk_clipboard_get_formats(window->clipboard); + can_paste = gdk_content_formats_contain_gtype(formats, G_TYPE_STRING); +#endif /* VTE_GTK */ auto action = g_action_map_lookup_action(G_ACTION_MAP(window), "paste"); g_simple_action_set_enabled(G_SIMPLE_ACTION(action), can_paste); @@ -1603,10 +1859,15 @@ window_action_copy_match_cb(GSimpleAction* action, GVariant* parameter, void* data) { - VteappWindow* window = VTEAPP_WINDOW(data); - gsize len; - char const* str = g_variant_get_string(parameter, &len); + auto window = VTEAPP_WINDOW(data); + + auto len = size_t{}; + auto str = g_variant_get_string(parameter, &len); +#if VTE_GTK == 3 gtk_clipboard_set_text(window->clipboard, str, len); +#elif VTE_GTK == 4 + gdk_clipboard_set_text(window->clipboard, str); +#endif } static void @@ -1623,16 +1884,23 @@ window_action_reset_cb(GSimpleAction* action, GVariant* parameter, void* data) { - VteappWindow* window = VTEAPP_WINDOW(data); - bool clear; - GdkModifierType modifiers; + auto window = VTEAPP_WINDOW(data); + auto clear = false; if (parameter != nullptr) clear = g_variant_get_boolean(parameter); - else if (gtk_get_current_event_state(&modifiers)) + else { + auto modifiers = GdkModifierType{}; +#if VTE_GTK == 3 + if (!gtk_get_current_event_state(&modifiers)) + modifiers = GdkModifierType(0); +#elif VTE_GTK == 4 + // FIXMEgtk4! + modifiers = GdkModifierType(0); +#endif + clear = (modifiers & GDK_CONTROL_MASK) != 0; - else - clear = false; + } vte_terminal_reset(window->terminal, true, clear); } @@ -1646,7 +1914,6 @@ window_action_find_cb(GSimpleAction* action, gtk_toggle_button_set_active(window->find_button, true); } - static void window_action_fullscreen_state_cb (GSimpleAction *action, GVariant *state, @@ -1665,6 +1932,7 @@ window_action_fullscreen_state_cb (GSimpleAction *action, /* The window-state-changed callback will update the action's actual state */ } +#if VTE_GTK == 3 static bool vteapp_window_show_context_menu(VteappWindow* window, guint button, @@ -1678,20 +1946,21 @@ vteapp_window_show_context_menu(VteappWindow* window, g_menu_append(menu, "_Copy", "win.copy::text"); g_menu_append(menu, "Copy As _HTML", "win.copy::html"); - if (event != nullptr) { - auto hyperlink = vte_terminal_hyperlink_check_event(window->terminal, event); - if (hyperlink != nullptr) { - verbose_print("Hyperlink: %s\n", hyperlink); - auto target = g_variant_new_string(hyperlink); /* floating */ + if (event != nullptr) + { + auto hyperlink = vte::glib::take_string(vte_terminal_hyperlink_check_event(window->terminal, event)); + if (hyperlink) { + verbose_print("Hyperlink: %s\n", hyperlink.get()); + auto target = g_variant_new_string(hyperlink.get()); /* floating */ auto item = vte::glib::take_ref(g_menu_item_new("Copy _Hyperlink", nullptr)); g_menu_item_set_action_and_target_value(item.get(), "win.copy-match", target); g_menu_append_item(menu, item.get()); } - auto match = vte_terminal_match_check_event(window->terminal, event, nullptr); - if (match != nullptr) { - verbose_print("Match: %s\n", match); - auto target = g_variant_new_string(match); /* floating */ + auto match = vte::glib::take_string(vte_terminal_match_check_event(window->terminal, event, nullptr)); + if (match) { + verbose_print("Match: %s\n", match.get()); + auto target = g_variant_new_string(match.get()); /* floating */ auto item = vte::glib::take_ref(g_menu_item_new("Copy _Match", nullptr)); g_menu_item_set_action_and_target_value(item.get(), "win.copy-match", target); g_menu_append_item(menu, item.get()); @@ -1725,15 +1994,14 @@ vteapp_window_show_context_menu(VteappWindow* window, else verbose_print("%s match: %s\n", extra_pattern, extra_match); } - g_free(hyperlink); - g_free(match); g_free(extra_match); g_free(extra_subst); } g_menu_append(menu, "_Paste", "win.paste"); - if (window->fullscreen) + auto const fullscreen = (window->window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0; + if (fullscreen) g_menu_append(menu, "_Fullscreen", "win.fullscreen"); auto popup = gtk_menu_new_from_model(G_MENU_MODEL(menu)); @@ -1744,13 +2012,23 @@ vteapp_window_show_context_menu(VteappWindow* window, return true; } +#endif /* VTE_GTK */ + +#if VTE_GTK == 3 static gboolean window_popup_menu_cb(GtkWidget* widget, VteappWindow* window) { - return vteapp_window_show_context_menu(window, 0, gtk_get_current_event_time(), nullptr); + auto const timestamp = gtk_get_current_event_time(); + + return vteapp_window_show_context_menu(window, 0, timestamp , nullptr); } +// FIXMEgtk4 + +#endif /* VTE_GTK == 3 */ + +#if VTE_GTK == 3 static gboolean window_button_press_cb(GtkWidget* widget, @@ -1764,6 +2042,8 @@ window_button_press_cb(GtkWidget* widget, reinterpret_cast<GdkEvent*>(event)); } +#endif /* VTE_GTK == 3 */ + static void window_cell_size_changed_cb(VteTerminal* term, guint width, @@ -1815,9 +2095,15 @@ window_child_exited_cb(VteTerminal* term, if (options.keep) return; +#if VTE_GTK == 3 gtk_widget_destroy(GTK_WIDGET(window)); +#elif VTE_GTK == 4 + gtk_window_destroy(GTK_WINDOW(window)); +#endif } +#if VTE_GTK == 3 + static void window_clipboard_owner_change_cb(GtkClipboard* clipboard, GdkEvent* event, @@ -1826,6 +2112,18 @@ window_clipboard_owner_change_cb(GtkClipboard* clipboard, window_update_paste_sensitivity(window); } +#elif VTE_GTK == 4 + +static void +window_clipboard_formats_notify_cb(GdkClipboard* clipboard, + GParamSpec* pspec, + VteappWindow* window) +{ + window_update_paste_sensitivity(window); +} + +#endif /* VTE_GTK */ + static void window_decrease_font_size_cb(VteTerminal* terminal, VteappWindow* window) @@ -1847,7 +2145,12 @@ window_deiconify_window_cb(VteTerminal* terminal, if (!options.allow_window_ops) return; +#if VTE_GTK == 3 gtk_window_deiconify(GTK_WINDOW(window)); +#elif VTE_GTK == 4 + auto toplevel = GDK_TOPLEVEL(gtk_native_get_surface(GTK_NATIVE(window))); + gdk_toplevel_present(toplevel, nullptr); // FIXMEgtk4 nullptr not allowed +#endif } static void @@ -1857,26 +2160,25 @@ window_iconify_window_cb(VteTerminal* terminal, if (!options.allow_window_ops) return; +#if VTE_GTK == 3 gtk_window_iconify(GTK_WINDOW(window)); -} - -static void -window_icon_title_changed_cb(VteTerminal* terminal, - VteappWindow* window) -{ - if (!options.icon_title) - return; - - gdk_window_set_icon_name(gtk_widget_get_window(GTK_WIDGET(window)), - vte_terminal_get_icon_title(window->terminal)); +#elif VTE_GTK == 4 + auto toplevel = GDK_TOPLEVEL(gtk_native_get_surface(GTK_NATIVE(window))); + gdk_toplevel_minimize(toplevel); +#endif } static void window_window_title_changed_cb(VteTerminal* terminal, VteappWindow* window) { - gtk_window_set_title(GTK_WINDOW(window), - vte_terminal_get_window_title(window->terminal)); + auto const title = vte_terminal_get_window_title(window->terminal); +#if VTE_GTK == 3 + gtk_window_set_title(GTK_WINDOW(window), title); +#elif VTE_GTK == 4 + auto toplevel = GDK_TOPLEVEL(gtk_native_get_surface(GTK_NATIVE(window))); + gdk_toplevel_set_title(toplevel, title); +#endif } static void @@ -1888,7 +2190,12 @@ window_lower_window_cb(VteTerminal* terminal, if (!gtk_widget_get_realized(GTK_WIDGET(window))) return; +#if VTE_GTK == 3 gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window))); +#elif VTE_GTK == 4 + auto toplevel = GDK_TOPLEVEL(gtk_native_get_surface(GTK_NATIVE(window))); + gdk_toplevel_lower(toplevel); +#endif } static void @@ -1900,7 +2207,12 @@ window_raise_window_cb(VteTerminal* terminal, if (!gtk_widget_get_realized(GTK_WIDGET(window))) return; +#if VTE_GTK == 3 gdk_window_raise(gtk_widget_get_window(GTK_WIDGET(window))); +#elif VTE_GTK == 4 + auto toplevel = GDK_TOPLEVEL(gtk_native_get_surface(GTK_NATIVE(window))); + gdk_toplevel_present(toplevel, nullptr); // FIXMEgtk4 gdk_toplevel_raise() doesn't exist?? +#endif } static void @@ -1932,9 +2244,26 @@ window_move_window_cb(VteTerminal* terminal, if (!options.allow_window_ops) return; +#if VTE_GTK == 3 gtk_window_move(GTK_WINDOW(window), x, y); +#elif VTE_GTK == 4 + // FIXMEgtk4 +#endif } +#if VTE_GTK == 4 + +static void +window_toplevel_notify_state_cb(GdkToplevel* toplevel, + GParamSpec* pspec, + VteappWindow* window) +{ + window->toplevel_state = gdk_toplevel_get_state(toplevel); + window_update_fullscreen_state(window); +} + +#endif /* VTE_GTK == 4 */ + static void window_notify_cb(GObject* object, GParamSpec* pspec, @@ -2044,10 +2373,19 @@ vteapp_window_constructed(GObject *object) gtk_widget_set_margin_bottom(GTK_WIDGET(window->terminal), margin); } - gtk_range_set_adjustment(GTK_RANGE(window->scrollbar), - gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(window->terminal))); + auto vadj = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(window->terminal)); +#if VTE_GTK == 3 + gtk_range_set_adjustment(GTK_RANGE(window->scrollbar), vadj); +#elif VTE_GTK == 4 + gtk_scrollbar_set_adjustment(GTK_SCROLLBAR(window->scrollbar), vadj); +#endif + if (options.no_scrollbar) { +#if VTE_GTK == 3 gtk_widget_destroy(GTK_WIDGET(window->scrollbar)); +#elif VTE_GTK == 4 + // FIXMEgtk4 +#endif window->scrollbar = nullptr; } @@ -2070,39 +2408,58 @@ vteapp_window_constructed(GObject *object) g_action_map_add_action(map, G_ACTION(action.get())); g_signal_connect(action.get(), "notify::state", G_CALLBACK(window_input_enabled_state_cb), window); +#if VTE_GTK == 4 + auto gear_popover = gtk_menu_button_get_popover(GTK_MENU_BUTTON(window->gear_button)); + gtk_widget_set_halign(GTK_WIDGET(gear_popover), GTK_ALIGN_END); +#endif + /* Find */ window->search_popover = vteapp_search_popover_new(window->terminal, GTK_WIDGET(window->find_button)); + g_signal_connect(window->search_popover, "closed", G_CALLBACK(window_search_popover_closed_cb), window); g_signal_connect(window->find_button, "toggled", G_CALLBACK(window_find_button_toggled_cb), window); /* Clipboard */ +#if VTE_GTK == 3 window->clipboard = gtk_widget_get_clipboard(GTK_WIDGET(window), GDK_SELECTION_CLIPBOARD); g_signal_connect(window->clipboard, "owner-change", G_CALLBACK(window_clipboard_owner_change_cb), window); +#elif VTE_GTK == 4 + window->clipboard = gtk_widget_get_clipboard(GTK_WIDGET(window)); + g_signal_connect(window->clipboard, "notify::formats", G_CALLBACK(window_clipboard_formats_notify_cb), window); +#endif /* VTE_GTK */ /* Set ARGB visual */ if (options.transparency_percent >= 0) { +#if VTE_GTK == 3 if (!options.no_argb_visual) { auto screen = gtk_widget_get_screen(GTK_WIDGET(window)); auto visual = gdk_screen_get_rgba_visual(screen); if (visual != nullptr) gtk_widget_set_visual(GTK_WIDGET(window), visual); - } + } /* Without this transparency doesn't work; see bug #729884. */ gtk_widget_set_app_paintable(GTK_WIDGET(window), true); + +#elif VTE_GTK == 4 + // FIXMEgtk4 +#endif /* VTE_GTK == 3 */ } /* Signals */ +#if VTE_GTK == 3 g_signal_connect(window->terminal, "popup-menu", G_CALLBACK(window_popup_menu_cb), window); g_signal_connect(window->terminal, "button-press-event", G_CALLBACK(window_button_press_cb), window); +#elif VTE_GTK == 4 + // FIXMEgtk4 +#endif g_signal_connect(window->terminal, "char-size-changed", G_CALLBACK(window_cell_size_changed_cb), window); g_signal_connect(window->terminal, "child-exited", G_CALLBACK(window_child_exited_cb), window); g_signal_connect(window->terminal, "decrease-font-size", G_CALLBACK(window_decrease_font_size_cb), window); g_signal_connect(window->terminal, "deiconify-window", G_CALLBACK(window_deiconify_window_cb), window); - g_signal_connect(window->terminal, "icon-title-changed", G_CALLBACK(window_icon_title_changed_cb), window); g_signal_connect(window->terminal, "iconify-window", G_CALLBACK(window_iconify_window_cb), window); g_signal_connect(window->terminal, "increase-font-size", G_CALLBACK(window_increase_font_size_cb), window); g_signal_connect(window->terminal, "lower-window", G_CALLBACK(window_lower_window_cb), window); @@ -2118,11 +2475,13 @@ vteapp_window_constructed(GObject *object) g_signal_connect(window->terminal, "notify", G_CALLBACK(window_notify_cb), window); /* Settings */ +#if VTE_GTK == 3 if (options.no_double_buffer) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS; gtk_widget_set_double_buffered(GTK_WIDGET(window->terminal), false); G_GNUC_END_IGNORE_DEPRECATIONS; } +#endif /* VTE_GTK == 3 */ if (options.encoding != nullptr) { auto error = vte::glib::Error{}; @@ -2183,6 +2542,8 @@ vteapp_window_constructed(GObject *object) /* Done! */ gtk_grid_attach(GTK_GRID(window->window_grid), GTK_WIDGET(window->terminal), 0, 0, 1, 1); + gtk_widget_set_halign(GTK_WIDGET(window->terminal), GTK_ALIGN_FILL); + gtk_widget_set_valign(GTK_WIDGET(window->terminal), GTK_ALIGN_FILL); gtk_widget_show(GTK_WIDGET(window->terminal)); window_update_paste_sensitivity(window); @@ -2201,13 +2562,21 @@ vteapp_window_dispose(GObject *object) if (window->clipboard != nullptr) { g_signal_handlers_disconnect_by_func(window->clipboard, +#if VTE_GTK == 3 (void*)window_clipboard_owner_change_cb, +#elif VTE_GTK == 4 + (void*)window_clipboard_formats_notify_cb, +#endif window); window->clipboard = nullptr; } if (window->search_popover != nullptr) { +#if VTE_GTK == 3 gtk_widget_destroy(window->search_popover); +#elif VTE_GTK == 4 + gtk_widget_unparent(window->search_popover); // this destroys the popover +#endif /* VTE_GTK */ window->search_popover = nullptr; } @@ -2222,10 +2591,37 @@ vteapp_window_realize(GtkWidget* widget) /* Now we can know the CSD size, and thus apply the geometry. */ VteappWindow* window = VTEAPP_WINDOW(widget); verbose_print("VteappWindow::realize\n"); + +#if VTE_GTK == 3 + auto win = gtk_widget_get_window(GTK_WIDGET(window)); + window->window_state = gdk_window_get_state(win); +#elif VTE_GTK == 4 + auto surface = gtk_native_get_surface(GTK_NATIVE(widget)); + window->toplevel_state = gdk_toplevel_get_state(GDK_TOPLEVEL(surface)); + g_signal_connect(surface, "notify::state", + G_CALLBACK(window_toplevel_notify_state_cb), window); +#endif + + window_update_fullscreen_state(window); + vteapp_window_resize(window); } static void +vteapp_window_unrealize(GtkWidget* widget) +{ +#if VTE_GTK == 4 + auto window = VTEAPP_WINDOW(widget); + auto toplevel = gtk_native_get_surface(GTK_NATIVE(widget)); + g_signal_handlers_disconnect_by_func(toplevel, + (void*)window_toplevel_notify_state_cb, + window); +#endif + + GTK_WIDGET_CLASS(vteapp_window_parent_class)->unrealize(widget); +} + +static void vteapp_window_show(GtkWidget* widget) { GTK_WIDGET_CLASS(vteapp_window_parent_class)->show(widget); @@ -2236,6 +2632,8 @@ vteapp_window_show(GtkWidget* widget) vteapp_window_resize(window); } +#if VTE_GTK == 3 + static void vteapp_window_style_updated(GtkWidget* widget) { @@ -2251,18 +2649,17 @@ static gboolean vteapp_window_state_event (GtkWidget* widget, GdkEventWindowState* event) { - VteappWindow* window = VTEAPP_WINDOW(widget); + auto window = VTEAPP_WINDOW(widget); + window->window_state = event->new_window_state; - if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) { - window->fullscreen = (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0; - - auto action = reinterpret_cast<GSimpleAction*>(g_action_map_lookup_action(G_ACTION_MAP(window), "fullscreen")); - g_simple_action_set_state(action, g_variant_new_boolean (window->fullscreen)); - } + if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) + window_update_fullscreen_state(window); return GTK_WIDGET_CLASS(vteapp_window_parent_class)->window_state_event(widget, event); } +#endif /* VTE_GTK == 3 */ + static void vteapp_window_class_init(VteappWindowClass* klass) { @@ -2272,9 +2669,15 @@ vteapp_window_class_init(VteappWindowClass* klass) GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); widget_class->realize = vteapp_window_realize; + widget_class->unrealize = vteapp_window_unrealize; widget_class->show = vteapp_window_show; + +#if VTE_GTK == 3 widget_class->style_updated = vteapp_window_style_updated; widget_class->window_state_event = vteapp_window_state_event; +#elif VTE_GTK == 4 + // FIXMEgtk4 window state event +#endif gtk_widget_class_set_template_from_resource(widget_class, "/org/gnome/vte/app/ui/window.ui"); gtk_widget_class_set_css_name(widget_class, "vteapp-window"); @@ -2340,8 +2743,14 @@ app_action_close_cb(GSimpleAction* action, { GtkApplication* application = GTK_APPLICATION(data); auto window = gtk_application_get_active_window(application); - if (window != nullptr) - gtk_widget_destroy(GTK_WIDGET(window)); + if (window == nullptr) + return; + +#if VTE_GTK == 3 + gtk_widget_destroy(GTK_WIDGET(window)); +#elif VTE_GTK == 4 + gtk_window_destroy(GTK_WINDOW(window)); +#endif } static gboolean @@ -2381,16 +2790,24 @@ static void vteapp_application_init(VteappApplication* application) { g_object_set(gtk_settings_get_default(), - "gtk-enable-mnemonics", FALSE, "gtk-enable-accels", FALSE, +#if VTE_GTK == 3 + "gtk-enable-mnemonics", FALSE, /* Make gtk+ CSD not steal F10 from the terminal */ "gtk-menu-bar-accel", nullptr, +#endif nullptr); if (options.css) { +#if VTE_GTK == 3 gtk_style_context_add_provider_for_screen(gdk_screen_get_default (), GTK_STYLE_PROVIDER(options.css.get()), GTK_STYLE_PROVIDER_PRIORITY_USER); +#elif VTE_GTK == 4 + gtk_style_context_add_provider_for_display(gdk_display_get_default (), + GTK_STYLE_PROVIDER(options.css.get()), + GTK_STYLE_PROVIDER_PRIORITY_USER); +#endif } if (options.feed_stdin) { @@ -2491,8 +2908,11 @@ main(int argc, return EXIT_SUCCESS; } +#if VTE_GTK == 3 if (options.debug) gdk_window_set_debug_updates(true); +#endif /* VTE_GTK == 3 */ + #ifdef VTE_DEBUG if (options.test_mode) { vte_set_test_flags(VTE_TEST_FLAGS_ALL); diff --git a/src/app/appmenu-gtk4.ui b/src/app/appmenu-gtk4.ui new file mode 100644 index 00000000..ac64ebb3 --- /dev/null +++ b/src/app/appmenu-gtk4.ui @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright © 2017, 2020 Christian Persch + + This program 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. + + This program 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 this program. If not, see <https://www.gnu.org/licenses/>. +--> +<interface> + <menu id="app-menu"> + <section> + <item> + <attribute name="label" translatable="yes">_New Terminal</attribute> + <attribute name="action">app.new</attribute> + </item> + </section> + <section> + <item> + <attribute name="label" translatable="yes">_Close</attribute> + <attribute name="action">app.close</attribute> + </item> + </section> + </menu> +</interface> diff --git a/src/app/meson.build b/src/app/meson.build index f7df6dbf..dd9c713c 100644 --- a/src/app/meson.build +++ b/src/app/meson.build @@ -44,11 +44,11 @@ if get_option('gtk3') ) app_gtk3_sources = app_sources + app_gtk3_resource_sources - app_gtk3_cppflags = app_common_cppflags + gtk3_version_cppflags + app_gtk3_cppflags = app_common_cppflags + gtk3_version_cppflags + ['-DVTE_GTK=3',] app_gtk3_deps = app_common_deps + [libvte_gtk3_dep] app_gtk3 = executable( - 'vte-' + vte_gtk3_api_version, + 'vte-' + vte_api_version, app_gtk3_sources, dependencies: app_gtk3_deps, cpp_args: app_gtk3_cppflags, @@ -56,3 +56,32 @@ if get_option('gtk3') install: true, ) endif + +if get_option('gtk4') + + app_gtk4_resource_data = files( + 'appmenu-gtk4.ui', + 'search-popover-gtk4.ui', + 'window-gtk4.ui', + ) + + app_gtk4_resource_sources = gnome.compile_resources( + 'resources-gtk4.cc', + 'app-gtk4.gresource.xml', + c_name: 'app', + dependencies: app_gtk4_resource_data, + ) + + app_gtk4_sources = app_sources + [app_gtk4_resource_sources,] + app_gtk4_cppflags = app_common_cppflags + gtk4_version_cppflags + ['-DVTE_GTK=4',] + app_gtk4_deps = app_common_deps + [libvte_gtk4_dep] + + app_gtk4 = executable( + 'vte-' + vte_api_version + '-gtk4', + app_gtk4_sources, + dependencies: app_gtk4_deps, + cpp_args: app_gtk4_cppflags, + include_directories: top_inc, + install: true, + ) +endif diff --git a/src/app/search-popover-gtk3.ui b/src/app/search-popover-gtk3.ui index d7191b86..fbad2d0b 100644 --- a/src/app/search-popover-gtk3.ui +++ b/src/app/search-popover-gtk3.ui @@ -1,5 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.19.0 --> <!-- Copyright © 2016, 2017 Christian Persch diff --git a/src/app/search-popover-gtk4.ui b/src/app/search-popover-gtk4.ui new file mode 100644 index 00000000..4b187190 --- /dev/null +++ b/src/app/search-popover-gtk4.ui @@ -0,0 +1,169 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright © 2016, 2017, 2020 Christian Persch + + This program 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. + + This program 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 this program. If not, see <https://www.gnu.org/licenses/>. +--> +<interface> + <requires lib="gtk+" version="3.16"/> + <template class="VteappSearchPopover" parent="GtkPopover"> + <property name="can_focus">0</property> + <property name="child"> + <object class="GtkBox" id="box1"> + <property name="can_focus">0</property> + <property name="margin-start">12</property> + <property name="margin-end">12</property> + <property name="margin_top">12</property> + <property name="margin_bottom">12</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box2"> + <property name="can_focus">0</property> + <property name="spacing">18</property> + <child> + <object class="GtkBox" id="box4"> + <property name="hexpand">1</property> + <property name="can_focus">0</property> + <child> + <object class="GtkSearchEntry" id="search_entry"> + <property name="hexpand">1</property> + <property name="activates_default">1</property> + <property name="width_chars">30</property> + <property name="placeholder_text" translatable="yes">Search</property> + </object> + </child> + <child> + <object class="GtkButton" id="search_prev_button"> + <property name="receives_default">1</property> + <property name="tooltip_text" translatable="yes">Search for previous occurrence</property> + <property name="focus_on_click">0</property> + <child> + <object class="GtkImage" id="image2"> + <property name="can_focus">0</property> + <property name="icon_name">go-up-symbolic</property> + <property name="use_fallback">1</property> + </object> + </child> + </object> + </child> + <child> + <object class="GtkButton" id="search_next_button"> + <property name="receives_default">1</property> + <property name="tooltip_text" translatable="yes">Search for next occurrence</property> + <property name="focus_on_click">0</property> + <child> + <object class="GtkImage" id="image3"> + <property name="can_focus">0</property> + <property name="icon_name">go-down-symbolic</property> + <property name="use_fallback">1</property> + </object> + </child> + </object> + </child> + <style> + <class name="linked"/> + </style> + </object> + </child> + <child> + <object class="GtkToggleButton" id="reveal_button"> + <property name="receives_default">1</property> + <property name="tooltip_text" translatable="yes">Toggle search options</property> + <property name="focus_on_click">0</property> + <child> + <object class="GtkImage" id="image1"> + <property name="can_focus">0</property> + <property name="icon_name">open-menu-symbolic</property> + <property name="use_fallback">1</property> + </object> + </child> + <!-- + <accessibility> + <relation type="controller-for" target="revealer"/> + </accessibility> + --> + </object> + </child> + <child> + <object class="GtkButton" id="close_button"> + <property name="receives_default">1</property> + <property name="focus_on_click">0</property> + <child> + <object class="GtkImage" id="image4"> + <property name="can_focus">0</property> + <property name="icon_name">window-close-symbolic</property> + <property name="use_fallback">1</property> + </object> + </child> + </object> + </child> + </object> + </child> + <child> + <object class="GtkRevealer" id="revealer"> + <property name="can_focus">0</property> + <property name="transition_type">none</property> + <property name="reveal_child">0</property> + <property name="child"> + <object class="GtkBox" id="box3"> + <property name="can_focus">0</property> + <property name="margin_top">18</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkCheckButton" id="match_case_checkbutton"> + <property name="valign">center</property> + <property name="label" translatable="yes">_Match case</property> + <property name="use_underline">1</property> + <property name="focus_on_click">0</property> + <property name="halign">start</property> + <property name="valign">center</property> + </object> + </child> + <child> + <object class="GtkCheckButton" id="entire_word_checkbutton"> + <property name="valign">center</property> + <property name="label" translatable="yes">Match _entire word only</property> + <property name="use_underline">1</property> + <property name="focus_on_click">0</property> + <property name="halign">start</property> + </object> + </child> + <child> + <object class="GtkCheckButton" id="regex_checkbutton"> + <property name="valign">center</property> + <property name="label" translatable="yes">Match as _regular expression</property> + <property name="use_underline">1</property> + <property name="focus_on_click">0</property> + <property name="halign">start</property> + </object> + </child> + <child> + <object class="GtkCheckButton" id="wrap_around_checkbutton"> + <property name="valign">center</property> + <property name="label" translatable="yes">_Wrap around</property> + <property name="use_underline">1</property> + <property name="focus_on_click">0</property> + <property name="halign">start</property> + <property name="active">1</property> + </object> + </child> + </object> + </property> + </object> + </child> + </object> + </property> + </template> +</interface> diff --git a/src/app/window-gtk3.ui b/src/app/window-gtk3.ui index 29758b1a..b7bdb012 100644 --- a/src/app/window-gtk3.ui +++ b/src/app/window-gtk3.ui @@ -1,5 +1,4 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.19.0 --> <!-- Copyright © 2014, 2017 Christian Persch diff --git a/src/app/window-gtk4.ui b/src/app/window-gtk4.ui new file mode 100644 index 00000000..dc3c33a4 --- /dev/null +++ b/src/app/window-gtk4.ui @@ -0,0 +1,185 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright © 2014, 2017, 2020 Christian Persch + + This program 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. + + This program 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 this program. If not, see <https://www.gnu.org/licenses/>. +--> +<interface> + <requires lib="gtk+" version="3.10"/> + <menu id="gear_menu_model"> + <section> + <item> + <attribute name="label" translatable="yes">_New Terminal</attribute> + <attribute name="action">app.new</attribute> + </item> + </section> + <section> + <item> + <attribute name="label" translatable="yes">_Copy</attribute> + <attribute name="action">win.copy</attribute> + <attribute name="target">text</attribute> + </item> + <item> + <attribute name="label" translatable="yes">Copy As _HTML</attribute> + <attribute name="action">win.copy</attribute> + <attribute name="target">html</attribute> + </item> + <item> + <attribute name="label" translatable="yes">_Paste</attribute> + <attribute name="action">win.paste</attribute> + </item> + </section> + <section> + <item> + <attribute name="label" translatable="yes">_Find…</attribute> + <attribute name="action">win.find</attribute> + </item> + </section> + <section> + <item> + <attribute name="label" translatable="yes">_Reset</attribute> + <attribute name="action">win.reset</attribute> + <attribute name="target" type="b">false</attribute> + </item> + <item> + <attribute name="label" translatable="yes">Reset and Cl_ear</attribute> + <attribute name="action">win.reset</attribute> + <attribute name="target" type="b">true</attribute> + </item> + <item> + <attribute name="label" translatable="yes">_Input enabled</attribute> + <attribute name="action">win.input-enabled</attribute> + </item> + </section> + <section> + <item> + <attribute name="label" translatable="yes">_Fullscreen</attribute> + <attribute name="action">win.fullscreen</attribute> + </item> + </section> + </menu> + <template class="VteappWindow" parent="GtkApplicationWindow"> + <property name="can_focus">0</property> + <property name="icon_name">utilities-terminal</property> + <child> + <object class="GtkGrid" id="window_grid"> + <property name="can_focus">0</property> + <property name="halign">fill</property> + <property name="valign">fill</property> + <property name="hexpand">1</property> + <property name="vexpand">1</property> + <child> + <placeholder/> + </child> + <child> + <object class="GtkScrollbar" id="scrollbar"> + <property name="can_focus">0</property> + <property name="orientation">vertical</property> + <property name="hexpand">0</property> + <property name="vexpand">1</property> + <layout> + <property name="column">1</property> + <property name="row">0</property> + </layout> + </object> + </child> + </object> + </child> + <child type="titlebar"> + <object class="GtkHeaderBar" id="headerbar"> + <property name="can_focus">0</property> + <property name="decoration_layout">:close</property> + <child type="start"> + <object class="GtkButton" id="copy_button"> + <property name="receives_default">1</property> + <property name="tooltip_text" translatable="yes">Copy</property> + <property name="action_name">win.copy</property> + <property name="action_target">"text"</property> + <property name="focus_on_click">0</property> + <child> + <object class="GtkImage" id="image2"> + <property name="can_focus">0</property> + <property name="icon_name">edit-copy-symbolic</property> + <property name="use_fallback">1</property> + </object> + </child> + </object> + </child> + <child type="start"> + <object class="GtkButton" id="paste_button"> + <property name="receives_default">1</property> + <property name="tooltip_text" translatable="yes">Paste</property> + <property name="action_name">win.paste</property> + <property name="focus_on_click">0</property> + <child> + <object class="GtkImage" id="image3"> + <property name="can_focus">0</property> + <property name="icon_name">edit-paste-symbolic</property> + <property name="use_fallback">1</property> + </object> + </child> + </object> + </child> + <child type="start"> + <object class="GtkToggleButton" id="find_button"> + <property name="receives_default">1</property> + <property name="focus_on_click">0</property> + <child> + <object class="GtkImage" id="image5"> + <property name="can_focus">0</property> + <property name="icon_name">edit-find-symbolic</property> + <property name="use_fallback">1</property> + </object> + </child> + </object> + </child> + <child type="title"> + <placeholder/> + </child> + <child type="end"> + <object class="GtkMenuButton" id="gear_button"> + <property name="receives_default">1</property> + <property name="focus_on_click">0</property> + <property name="menu-model">gear_menu_model</property> + <property name="icon_name">open-menu-symbolic</property> + </object> + </child> + <child type="end"> + <object class="GtkGrid" id="notifications_grid"> + <property name="can_focus">0</property> + <property name="column_spacing">6</property> + <property name="row_spacing">6</property> + <property name="hexpand">0</property> + <property name="vexpand">1</property> + <child> + <object class="GtkImage" id="readonly_emblem"> + <property name="visible">0</property> + <property name="can_focus">0</property> + <property name="tooltip_text" translatable="yes">Read-only</property> + <property name="icon_name">emblem-readonly</property> + <property name="use_fallback">1</property> + <property name="hexpand">0</property> + <property name="vexpand">0</property> + <layout> + <property name="column">0</property> + <property name="row">0</property> + </layout> + </object> + </child> + </object> + </child> + </object> + </child> + </template> +</interface> diff --git a/src/cairo-glue.hh b/src/cairo-glue.hh index 234c8fcd..c6370e33 100644 --- a/src/cairo-glue.hh +++ b/src/cairo-glue.hh @@ -23,7 +23,9 @@ namespace vte { -VTE_DECLARE_FREEABLE(cairo_t, cairo_destroy); +VTE_DECLARE_FREEABLE(cairo_rectangle_list_t, cairo_rectangle_list_destroy); +VTE_DECLARE_FREEABLE(cairo_region_t, cairo_region_destroy); VTE_DECLARE_FREEABLE(cairo_surface_t, cairo_surface_destroy); +VTE_DECLARE_FREEABLE(cairo_t, cairo_destroy); } // namespace vte::cairo diff --git a/src/clipboard-gtk.cc b/src/clipboard-gtk.cc index a70c0eea..402e3a41 100644 --- a/src/clipboard-gtk.cc +++ b/src/clipboard-gtk.cc @@ -22,6 +22,7 @@ #include "widget.hh" #include "vteinternal.hh" +#include <new> #include <stdexcept> #include <utility> @@ -42,12 +43,20 @@ Clipboard::Clipboard(Widget& delegate, switch (type) { case ClipboardType::PRIMARY: - m_clipboard = vte::glib::make_ref(gtk_clipboard_get_for_display(display, - GDK_SELECTION_PRIMARY)); + m_clipboard = vte::glib::make_ref +#if VTE_GTK == 3 + (gtk_clipboard_get_for_display(display, GDK_SELECTION_PRIMARY)); +#elif VTE_GTK == 4 + (gdk_display_get_primary_clipboard(display)); +#endif break; case ClipboardType::CLIPBOARD: - m_clipboard = vte::glib::make_ref(gtk_clipboard_get_for_display(display, - GDK_SELECTION_CLIPBOARD)); + m_clipboard = vte::glib::make_ref +#if VTE_GTK == 3 + (gtk_clipboard_get_for_display(display, GDK_SELECTION_CLIPBOARD)); +#elif VTE_GTK == 4 + (gdk_display_get_clipboard(display)); +#endif break; } @@ -55,6 +64,8 @@ Clipboard::Clipboard(Widget& delegate, throw std::runtime_error{"Failed to create clipboard"}; } +#if VTE_GTK == 3 + class Clipboard::Offer { public: Offer(Clipboard& clipboard, @@ -75,7 +86,7 @@ public: { auto [targets, n_targets] = targets_for_format(format); - // Transfers clipboardship of *offer to the clipboard. If setting succeeds, + // Transfers ownership of *offer to the clipboard. If setting succeeds, // the clipboard will own *offer until the clipboard_data_clear_cb // callback is called. // If setting the clipboard fails, the clear callback will never be @@ -150,8 +161,8 @@ private: guint info, void* user_data) noexcept { - if (info != vte::to_integral(ClipboardFormat::TEXT) && - info != vte::to_integral(ClipboardFormat::HTML)) + if (int(info) != vte::to_integral(ClipboardFormat::TEXT) && + int(info) != vte::to_integral(ClipboardFormat::HTML)) return; reinterpret_cast<Offer*>(user_data)->dispatch_get(ClipboardFormat(info), data); @@ -161,12 +172,11 @@ private: clipboard_clear_cb(GtkClipboard* clipboard, void* user_data) noexcept { - // Assume ownership of the Request, and delete it after dispatching the callback + // Assume ownership of the Offer, and delete it after dispatching the callback auto offer = std::unique_ptr<Offer>{reinterpret_cast<Offer*>(user_data)}; offer->dispatch_clear(); } - static std::pair<GtkTargetEntry*, int> targets_for_format(ClipboardFormat format) { @@ -227,6 +237,8 @@ private: }; // class Clipboard::Offer +#endif /* VTE_GTK == 3 */ + class Clipboard::Request { public: Request(Clipboard& clipboard, @@ -244,10 +256,12 @@ public: static void run(std::unique_ptr<Request> request) noexcept { +#if VTE_GTK == 3 auto platform = request->clipboard().platform(); gtk_clipboard_request_text(platform, text_received_cb, request.release()); +#endif /* VTE_GTK */ } private: @@ -255,6 +269,7 @@ private: RequestDoneCallback m_done_callback; RequestFailedCallback m_failed_callback; +#if VTE_GTK == 3 void dispatch(char const *text) noexcept try { @@ -278,6 +293,8 @@ private: request->dispatch(text); } +#endif /* VTE_GTK */ + }; // class Clipboard::Request void @@ -285,13 +302,17 @@ Clipboard::offer_data(ClipboardFormat format, OfferGetCallback get_callback, OfferClearCallback clear_callback) /* throws */ { +#if VTE_GTK == 3 Offer::run(std::make_unique<Offer>(*this, get_callback, clear_callback), format); +#endif } void Clipboard::set_text(std::string_view const& text) noexcept { +#if VTE_GTK == 3 gtk_clipboard_set_text(platform(), text.data(), text.size()); +#endif } void diff --git a/src/clipboard-gtk.hh b/src/clipboard-gtk.hh index d2b66228..45660c3e 100644 --- a/src/clipboard-gtk.hh +++ b/src/clipboard-gtk.hh @@ -75,7 +75,11 @@ public: RequestFailedCallback failed_callback) /* throws */; private: +#if VTE_GTK == 3 vte::glib::RefPtr<GtkClipboard> m_clipboard; +#elif VTE_GTK == 4 + vte::glib::RefPtr<GdkClipboard> m_clipboard; +#endif std::weak_ptr<Widget> m_delegate; ClipboardType m_type; diff --git a/src/debug.h b/src/debug.h index c91c4968..da1d51d8 100644 --- a/src/debug.h +++ b/src/debug.h @@ -113,6 +113,12 @@ static void _vte_debug_print(guint flags, const char *fmt, ...) #define _vte_debug_print(args...) do { } while(0) #endif /* VTE_DEBUG */ +static inline char const* +_vte_debug_tf(bool v) noexcept +{ + return v ? "true" : "false"; +} + G_END_DECLS #endif diff --git a/src/fonts-pangocairo.cc b/src/fonts-pangocairo.cc index fc0f4e54..995b441e 100644 --- a/src/fonts-pangocairo.cc +++ b/src/fonts-pangocairo.cc @@ -210,13 +210,13 @@ FontInfo::measure_font() } } -FontInfo::FontInfo(PangoContext *context) +FontInfo::FontInfo(vte::glib::RefPtr<PangoContext> context) { _vte_debug_print (VTE_DEBUG_PANGOCAIRO, "vtepangocairo: %p allocating FontInfo\n", (void*)this); - m_layout = vte::glib::take_ref(pango_layout_new(context)); + m_layout = vte::glib::take_ref(pango_layout_new(context.get())); auto tabs = pango_tab_array_new_with_positions(1, FALSE, PANGO_TAB_LEFT, 1); pango_layout_set_tabs(m_layout.get(), tabs); @@ -230,7 +230,7 @@ FontInfo::FontInfo(PangoContext *context) #if PANGO_VERSION_CHECK(1, 44, 0) /* Try using the font's metrics; see issue#163. */ if (auto metrics = vte::take_freeable - (pango_context_get_metrics(context, + (pango_context_get_metrics(context.get(), nullptr /* use font from context */, nullptr /* use language from context */))) { /* Use provided metrics if possible */ @@ -337,7 +337,7 @@ context_equal (PangoContext *a, && vte_pango_context_get_fontconfig_timestamp (a) == vte_pango_context_get_fontconfig_timestamp (b); } -/* assumes ownership/reference of context */ +// FIXMEchpe return vte::base::RefPtr<FontInfo> FontInfo* FontInfo::create_for_context(vte::glib::RefPtr<PangoContext> context, PangoFontDescription const* desc, @@ -381,14 +381,13 @@ FontInfo::create_for_context(vte::glib::RefPtr<PangoContext> context, info); info = info->ref(); } else { - _vte_debug_print (VTE_DEBUG_PANGOCAIRO, - "vtepangocairo: FontInfo not in cache\n"); - info = new FontInfo{context.get()}; - } + info = new FontInfo{std::move(context)}; + } return info; } +#if VTE_GTK == 3 FontInfo* FontInfo::create_for_screen(GdkScreen* screen, PangoFontDescription const* desc, @@ -400,15 +399,28 @@ FontInfo::create_for_screen(GdkScreen* screen, return create_for_context(vte::glib::take_ref(gdk_pango_context_get_for_screen(screen)), desc, language, fontconfig_timestamp); } +#endif /* VTE_GTK */ FontInfo* FontInfo::create_for_widget(GtkWidget* widget, PangoFontDescription const* desc) { - auto screen = gtk_widget_get_screen(widget); - auto language = pango_context_get_language(gtk_widget_get_pango_context(widget)); + auto context = gtk_widget_get_pango_context(widget); + auto language = pango_context_get_language(context); +#if VTE_GTK == 3 + auto screen = gtk_widget_get_screen(widget); return create_for_screen(screen, desc, language); +#elif VTE_GTK == 4 + auto display = gtk_widget_get_display(widget); + auto settings = gtk_settings_get_for_display(display); + auto fontconfig_timestamp = guint{}; + g_object_get (settings, "gtk-fontconfig-timestamp", &fontconfig_timestamp, nullptr); + return create_for_context(vte::glib::make_ref(context), + desc, language, fontconfig_timestamp); + // FIXMEgtk4: this uses a per-widget context, while the gtk3 code uses a per-screen + // one. That means there may be a lot less sharing and a lot more FontInfo's around? +#endif } FontInfo::UnistrInfo* diff --git a/src/fonts-pangocairo.hh b/src/fonts-pangocairo.hh index c7aac75c..586e6668 100644 --- a/src/fonts-pangocairo.hh +++ b/src/fonts-pangocairo.hh @@ -120,7 +120,7 @@ class FontInfo { int const font_cache_timeout = 30; // seconds public: - FontInfo(PangoContext* context); + FontInfo(vte::glib::RefPtr<PangoContext> context); ~FontInfo(); FontInfo* ref() @@ -269,9 +269,12 @@ private: PangoFontDescription const* desc, PangoLanguage* language, guint fontconfig_timestamp); +#if VTE_GTK == 3 static FontInfo *create_for_screen(GdkScreen* screen, PangoFontDescription const* desc, PangoLanguage* language); +#endif + public: static FontInfo *create_for_widget(GtkWidget* widget, diff --git a/src/graphene-glue.hh b/src/graphene-glue.hh new file mode 100644 index 00000000..16456117 --- /dev/null +++ b/src/graphene-glue.hh @@ -0,0 +1,48 @@ +/* + * Copyright © 2020 Christian Persch + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <cairo.h> +#include <graphene.h> + +#include "std-glue.hh" + +namespace vte::graphene { + +inline constexpr auto +make_rect(int x, + int y, + int width, + int height) +{ + return GRAPHENE_RECT_INIT(float(x), float(y), float(width), float(height)); +} + +inline constexpr auto +make_rect(cairo_rectangle_int_t const* rect) +{ + return make_rect(rect->x, rect->y, rect->width, rect->height); +} + +} // namespace vte::graphene + +namespace vte { + +// VTE_DECLARE_FREEABLE(graphene_rect_t, graphene_rect_free); + +} // namespace vte::cairo diff --git a/src/gtk-glue.hh b/src/gtk-glue.hh index 1634beb6..71ffc9fd 100644 --- a/src/gtk-glue.hh +++ b/src/gtk-glue.hh @@ -25,6 +25,12 @@ namespace vte::gtk { namespace vte { +#if VTE_GTK == 3 VTE_DECLARE_FREEABLE(GtkTargetList, gtk_target_list_unref); +#endif /* VTE_GTK == 3 */ + +#if VTE_GTK == 4 +VTE_DECLARE_FREEABLE(GdkContentFormats, gdk_content_formats_unref); +#endif /* VTE_GTK == 4 */ } // namespace vte diff --git a/src/keymap.h b/src/keymap.h index 3feef90e..4eface40 100644 --- a/src/keymap.h +++ b/src/keymap.h @@ -25,8 +25,13 @@ G_BEGIN_DECLS +#if VTE_GTK == 3 #define VTE_ALT_MASK GDK_MOD1_MASK #define VTE_NUMLOCK_MASK GDK_MOD2_MASK +#elif VTE_GTK == 4 +#define VTE_ALT_MASK GDK_ALT_MASK +#define VTE_NUMLOCK_MASK 0 /* FIXME */ +#endif /* Map the specified keyval/modifier setup, dependent on the mode, to either * a literal string or a capability name. */ diff --git a/src/meson.build b/src/meson.build index bfbeebb2..e9192be0 100644 --- a/src/meson.build +++ b/src/meson.build @@ -32,6 +32,10 @@ glib_glue_sources = files( 'glib-glue.hh', ) +graphene_glue_sources = files( + 'graphene-glue.hh', +) + gtk_glue_sources = files( 'gtk-glue.hh', ) @@ -212,10 +216,6 @@ libvte_common_sources = config_sources + debug_sources + glib_glue_sources + gtk 'widget.hh', ) -if get_option('a11y') - libvte_common_sources += a11y_sources -endif - if get_option('icu') libvte_common_sources += icu_sources endif @@ -294,9 +294,13 @@ libvte_common_cppflags = [ if get_option('gtk3') libvte_gtk3_sources = libvte_common_sources + libvte_gtk3_public_headers + libvte_gtk3_enum_sources - libvte_gtk3_cppflags = libvte_common_cppflags + gtk3_version_cppflags - libvte_gtk3_deps = libvte_common_deps + [gtk3_dep] - libvte_gtk3_public_deps = libvte_common_public_deps + [gtk3_dep] + libvte_gtk3_cppflags = libvte_common_cppflags + gtk3_version_cppflags + ['-DVTE_GTK=3',] + libvte_gtk3_deps = libvte_common_deps + [gtk3_dep,] + libvte_gtk3_public_deps = libvte_common_public_deps + [gtk3_dep,] + + if get_option('a11y') + libvte_gtk3_sources += a11y_sources + endif libvte_gtk3 = shared_library( vte_gtk3_api_name, @@ -310,7 +314,7 @@ if get_option('gtk3') libvte_gtk3_dep = declare_dependency( sources: libvte_gtk3_public_headers, - include_directories: [src_inc, vte_inc], + include_directories: [src_inc, vte_inc,], dependencies: libvte_gtk3_deps, link_with: libvte_gtk3 ) @@ -327,6 +331,41 @@ if get_option('gtk3') ) endif +if get_option('gtk4') + libvte_gtk4_sources = libvte_common_sources + libvte_gtk4_public_headers + libvte_gtk4_enum_sources + graphene_glue_sources + libvte_gtk4_cppflags = libvte_common_cppflags + gtk4_version_cppflags + ['-DVTE_GTK=4',] + libvte_gtk4_deps = libvte_common_deps + [gtk4_dep,] + libvte_gtk4_public_deps = libvte_common_public_deps + [gtk4_dep,] + + libvte_gtk4 = shared_library( + vte_gtk4_api_name, + sources: libvte_gtk4_sources, + version: libvte_gtk4_soversion, + include_directories: incs, + dependencies: libvte_gtk4_deps, + cpp_args: libvte_gtk4_cppflags, + install: true, + ) + + libvte_gtk4_dep = declare_dependency( + sources: libvte_gtk4_public_headers, + include_directories: [src_inc, vte_inc,], + dependencies: libvte_gtk4_deps, + link_with: libvte_gtk4 + ) + + pkg.generate( + libvte_gtk4, + version: vte_version, + name: 'vte', + description: 'VTE widget for GTK+ 4.0', + filebase: vte_gtk4_api_name, + subdirs: vte_gtk4_api_name, + requires: libvte_gtk4_public_deps, + variables: 'exec_prefix=${prefix}', + ) +endif + ## Tests # decoder cat @@ -425,17 +464,19 @@ reflect_textview = executable( install: false, ) -reflect_vte = executable( - 'reflect-vte', - sources: reflect_sources, - dependencies: [gtk3_dep, libvte_gtk3_dep], - c_args: [ - '-DUSE_VTE', - '-DVTE_DISABLE_DEPRECATION_WARNINGS', - ], - include_directories: top_inc, - install: false, -) +if get_option('gtk3') + reflect_vte = executable( + 'reflect-vte', + sources: reflect_sources, + dependencies: [gtk3_dep, libvte_gtk3_dep,], + c_args: [ + '-DUSE_VTE', + '-DVTE_DISABLE_DEPRECATION_WARNINGS', + ], + include_directories: [top_inc,], + install: false, + ) +endif # vte-urlencode-cwd @@ -599,14 +640,16 @@ test_vtetypes_sources = config_sources + libc_glue_sources + files( 'vtetypes.hh', ) -test_vtetypes = executable( - 'test-vtetypes', - sources: test_vtetypes_sources, - dependencies: [glib_dep, pango_dep, gtk3_dep], - cpp_args: ['-DMAIN'], - include_directories: top_inc, - install: false, -) +if get_option('gtk3') + test_vtetypes = executable( + 'test-vtetypes', + sources: test_vtetypes_sources, + dependencies: [glib_dep, pango_dep, gtk3_dep,], + cpp_args: ['-DMAIN'], + include_directories: top_inc, + install: false, + ) +endif test_env = [ 'VTE_DEBUG=0' @@ -621,9 +664,14 @@ test_units = [ ['stream', test_stream], ['tabstops', test_tabstops], ['utf8', test_utf8], - ['vtetypes', test_vtetypes], ] +if get_option('gtk3') + test_units += [ + ['vtetypes', test_vtetypes], + ] +endif + if get_option('sixel') test_units += [ ['sixel', test_sixel], @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004,2009,2010 Red Hat, Inc. - * Copyright © 2008, 2009, 2010 Christian Persch + * Copyright © 2008, 2009, 2010, 2020 Christian Persch * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -49,6 +49,7 @@ #include "ringview.hh" #include "caps.hh" #include "widget.hh" +#include "cairo-glue.hh" #ifdef HAVE_WCHAR_H #include <wchar.h> @@ -72,8 +73,12 @@ #include "gobject-glue.hh" #ifdef WITH_A11Y +#if VTE_GTK == 3 #include "vteaccess.h" -#endif +#else +#undef WITH_A11Y +#endif /* VTE_GTK == 3 */ +#endif /* WITH_A11Y */ #include <new> /* placement new */ @@ -95,6 +100,12 @@ static inline double round(double x) { #define VTE_DRAW_OPAQUE (1.0) +#if VTE_GTK == 3 +#define VTE_STYLE_CLASS_READ_ONLY GTK_STYLE_CLASS_READ_ONLY +#elif VTE_GTK == 4 +#define VTE_STYLE_CLASS_READ_ONLY "read-only" +#endif + namespace vte { namespace terminal { @@ -106,7 +117,10 @@ static void remove_update_timeout(vte::terminal::Terminal* that); static gboolean process_timeout (gpointer data) noexcept; static gboolean update_timeout (gpointer data) noexcept; -static cairo_region_t *vte_cairo_get_clip_region (cairo_t *cr); + +#if VTE_GTK == 3 +static vte::Freeable<cairo_region_t> vte_cairo_get_clip_region(cairo_t* cr); +#endif /* these static variables are guarded by the GDK mutex */ static guint process_timeout_tag = 0; @@ -438,7 +452,11 @@ Terminal::invalidate_rows(vte::grid::row_t row_start, rect.x += allocation.x + m_padding.left; rect.y += allocation.y + m_padding.top; cairo_region_t *region = cairo_region_create_rectangle(&rect); +#if VTE_GTK == 3 gtk_widget_queue_draw_region(m_widget, region); +#elif VTE_GTK == 4 + gtk_widget_queue_draw(m_widget); // FIXMEchpe +#endif cairo_region_destroy(region); } @@ -1528,6 +1546,9 @@ Terminal::regex_match_check(vte::grid::column_t column, vte::grid::row_t row, int* tag) { + /* Need to ensure the ringview is updated. */ + ringview_update(); + long delta = m_screen->scroll_delta; _vte_debug_print(VTE_DEBUG_EVENTS | VTE_DEBUG_REGEX, "Checking for match at (%ld,%ld).\n", @@ -1568,7 +1589,11 @@ Terminal::regex_match_check(vte::grid::column_t column, vte::view::coords Terminal::view_coords_from_event(vte::platform::MouseEvent const& event) const { +#if VTE_GTK == 3 return vte::view::coords(event.x() - m_padding.left, event.y() - m_padding.top); +#elif VTE_GTK == 4 + return vte::view::coords(event.x(), event.y()); +#endif } bool @@ -1816,10 +1841,50 @@ Terminal::rowcol_from_event(vte::platform::MouseEvent const& event, return true; } -char * +#if VTE_GTK == 4 + +bool +Terminal::rowcol_at(double x, + double y, + long* column, + long* row) +{ + auto rowcol = grid_coords_from_view_coords(vte::view::coords(x, y)); + if (!grid_coords_visible(rowcol)) + return false; + + *column = rowcol.column(); + *row = rowcol.row(); + return true; +} + +char* +Terminal::hyperlink_check_at(double x, + double y) +{ + long col, row; + if (!rowcol_at(x, y, &col, &row)) + return nullptr; + + return hyperlink_check(col, row); +} + +#endif /* VTE_GTK == 4 */ + +char* Terminal::hyperlink_check(vte::platform::MouseEvent const& event) { long col, row; + if (!rowcol_from_event(event, &col, &row)) + return nullptr; + + return hyperlink_check(col, row); +} + +char* +Terminal::hyperlink_check(vte::grid::column_t col, + vte::grid::row_t row) +{ const char *hyperlink; const char *separator; @@ -1829,9 +1894,6 @@ Terminal::hyperlink_check(vte::platform::MouseEvent const& event) /* Need to ensure the ringview is updated. */ ringview_update(); - if (!rowcol_from_event(event, &col, &row)) - return NULL; - _vte_ring_get_hyperlink_at_position(m_screen->row_data, row, col, false, &hyperlink); if (hyperlink != NULL) { @@ -1848,15 +1910,12 @@ Terminal::hyperlink_check(vte::platform::MouseEvent const& event) return g_strdup(hyperlink); } -char * +char* Terminal::regex_match_check(vte::platform::MouseEvent const& event, int *tag) { long col, row; - /* Need to ensure the ringview is updated. */ - ringview_update(); - if (!rowcol_from_event(event, &col, &row)) return FALSE; @@ -1872,9 +1931,23 @@ Terminal::regex_match_check_extra(vte::platform::MouseEvent const& event, uint32_t match_flags, char** matches) { + long col, row; + if (!rowcol_from_event(event, &col, &row)) + return false; + + return regex_match_check_extra(col, row, regexes, n_regexes, match_flags, matches); +} + +bool +Terminal::regex_match_check_extra(vte::grid::column_t col, + vte::grid::row_t row, + vte::base::Regex const** regexes, + size_t n_regexes, + uint32_t match_flags, + char** matches) +{ gsize offset, sattr, eattr; bool any_matches = false; - long col, row; guint i; assert(regexes != nullptr || n_regexes == 0); @@ -1883,9 +1956,6 @@ Terminal::regex_match_check_extra(vte::platform::MouseEvent const& event, /* Need to ensure the ringview is updated. */ ringview_update(); - if (!rowcol_from_event(event, &col, &row)) - return false; - if (m_match_contents == nullptr) { match_contents_refresh(); } @@ -4583,10 +4653,11 @@ Terminal::beep() bool Terminal::widget_key_press(vte::platform::KeyEvent const& event) { + auto handled = false; char *normal = NULL; gsize normal_length = 0; struct termios tio; - gboolean scrolled = FALSE, steal = FALSE, modifier = FALSE, handled, + gboolean scrolled = FALSE, steal = FALSE, modifier = FALSE, suppress_alt_esc = FALSE, add_modifiers = FALSE; guint keyval = 0; gunichar keychar = 0; @@ -4614,12 +4685,6 @@ Terminal::widget_key_press(vte::platform::KeyEvent const& event) set_pointer_autohidden(true); } - _vte_debug_print(VTE_DEBUG_EVENTS, - "Keypress, modifiers=0x%x, " - "keyval=0x%x\n", - m_modifiers, - keyval); - /* We steal many keypad keys here. */ if (!m_im_preedit_active) { switch (keyval) { @@ -4709,6 +4774,7 @@ Terminal::widget_key_press(vte::platform::KeyEvent const& event) /* Let the input method at this one first. */ if (!steal && m_input_enabled) { + // FIXMEchpe FIXMEgtk4: update IM position? im_set_cursor_location() if (m_real_widget->im_filter_keypress(event)) { _vte_debug_print(VTE_DEBUG_EVENTS, "Keypress taken by IM.\n"); @@ -5040,6 +5106,16 @@ Terminal::widget_key_press(vte::platform::KeyEvent const& event) } return true; } + +#if VTE_GTK == 4 + if (!handled && + event.matches(GDK_KEY_Menu, 0)) { + _vte_debug_print(VTE_DEBUG_EVENTS, "Showing context menu\n"); + // FIXMEgtk4 do context menu + handled = true; + } +#endif + return false; } @@ -5960,6 +6036,8 @@ Terminal::invalidate_match_span() void Terminal::match_hilite_update() { + _vte_debug_print(VTE_DEBUG_EVENTS, "Match hilite update\n"); + /* Need to ensure the ringview is updated. */ ringview_update(); @@ -5973,10 +6051,6 @@ Terminal::match_hilite_update() vte::base::BidiRow const* bidirow = m_ringview.get_bidirow(confine_grid_row(row)); col = bidirow->vis2log(col); - _vte_debug_print(VTE_DEBUG_EVENTS, - "Match hilite update (%ld, %ld) -> %ld, %ld\n", - pos.x, pos.y, col, row); - /* Whether there's any chance we'd highlight something */ bool do_check_hilite = view_coords_visible(pos) && m_mouse_cursor_over_widget && @@ -6768,8 +6842,7 @@ Terminal::widget_mouse_motion(vte::platform::MouseEvent const& event) auto rowcol = grid_coords_from_view_coords(pos); _vte_debug_print(VTE_DEBUG_EVENTS, - "Motion notify %s %s\n", - pos.to_string(), rowcol.to_string()); + "Motion grid %s\n", rowcol.to_string()); m_modifiers = event.modifiers(); @@ -6824,17 +6897,22 @@ Terminal::widget_mouse_press(vte::platform::MouseEvent const& event) /* Need to ensure the ringview is updated. */ ringview_update(); + /* Reset IM (like GtkTextView does) here */ + if (event.press_count() == 1) + widget()->im_reset(); + auto pos = view_coords_from_event(event); auto rowcol = grid_coords_from_view_coords(pos); + _vte_debug_print(VTE_DEBUG_EVENTS, + "Click gesture pressed button=%d at grid %s\n", + event.button_value(), + rowcol.to_string()); + m_modifiers = event.modifiers(); switch (event.press_count()) { case 1: /* single click */ - _vte_debug_print(VTE_DEBUG_EVENTS, - "Button %d single-click at %s\n", - event.button_value(), - rowcol.to_string()); /* Handle this event ourselves. */ switch (event.button()) { case vte::platform::MouseEvent::Button::eLEFT: @@ -6844,6 +6922,8 @@ Terminal::widget_mouse_press(vte::platform::MouseEvent const& event) if (!m_has_focus) widget()->grab_focus(); + // FIXMEchpe FIXMEgtk do im_reset() here + /* If we're in event mode, and the user held down the * shift key, we start selecting. */ if (m_mouse_tracking_mode != MouseTrackingMode::eNONE) { @@ -6904,10 +6984,6 @@ Terminal::widget_mouse_press(vte::platform::MouseEvent const& event) } break; case 2: /* double click */ - _vte_debug_print(VTE_DEBUG_EVENTS, - "Button %d double-click at %s\n", - event.button_value(), - rowcol.to_string()); switch (event.button()) { case vte::platform::MouseEvent::Button::eLEFT: if (m_will_select_after_threshold) { @@ -6928,10 +7004,6 @@ Terminal::widget_mouse_press(vte::platform::MouseEvent const& event) } break; case 3: /* triple click */ - _vte_debug_print(VTE_DEBUG_EVENTS, - "Button %d triple-click at %s\n", - event.button_value(), - rowcol.to_string()); switch (event.button()) { case vte::platform::MouseEvent::Button::eLEFT: if ((m_mouse_handled_buttons & 1) != 0) { @@ -6949,6 +7021,16 @@ Terminal::widget_mouse_press(vte::platform::MouseEvent const& event) break; } +#if VTE_GTK == 4 + if (!handled && + ((event.button() == vte::platform::MouseEvent::Button::eRIGHT) || + !(event.modifiers() & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK)))) { + _vte_debug_print(VTE_DEBUG_EVENTS, "Showing context menu\n"); + // FIXMEgtk4 context menu + handled = true; + } +#endif /* VTE_GTK == 4 */ + /* Save the pointer state for later use. */ if (event.button_value() >= 1 && event.button_value() <= 3) m_mouse_pressed_buttons |= (1 << (event.button_value() - 1)); @@ -6973,15 +7055,16 @@ Terminal::widget_mouse_release(vte::platform::MouseEvent const& event) auto pos = view_coords_from_event(event); auto rowcol = grid_coords_from_view_coords(pos); + _vte_debug_print(VTE_DEBUG_EVENTS, + "Click gesture released button=%d at grid %s\n", + event.button_value(), rowcol.to_string()); + stop_autoscroll(); m_modifiers = event.modifiers(); switch (event.type()) { case vte::platform::EventBase::Type::eMOUSE_RELEASE: - _vte_debug_print(VTE_DEBUG_EVENTS, - "Button %d released at %s\n", - event.button_value(), rowcol.to_string()); switch (event.button()) { case vte::platform::MouseEvent::Button::eLEFT: if ((m_mouse_handled_buttons & 1) != 0) @@ -7020,8 +7103,6 @@ Terminal::widget_mouse_release(vte::platform::MouseEvent const& event) void Terminal::widget_focus_in() { - _vte_debug_print(VTE_DEBUG_EVENTS, "Focus in.\n"); - m_has_focus = true; widget()->grab_focus(); @@ -7049,8 +7130,6 @@ Terminal::widget_focus_in() void Terminal::widget_focus_out() { - _vte_debug_print(VTE_DEBUG_EVENTS, "Focus out.\n"); - /* We only have an IM context when we're realized, and there's not much * point to painting ourselves if we don't have a window. */ if (widget_realized()) { @@ -7083,8 +7162,9 @@ Terminal::widget_mouse_enter(vte::platform::MouseEvent const& event) auto pos = view_coords_from_event(event); // FIXMEchpe read event modifiers here + // FIXMEgtk4 or maybe not since there is no event to read them from on gtk4 - _vte_debug_print(VTE_DEBUG_EVENTS, "Enter at %s\n", pos.to_string()); + _vte_debug_print(VTE_DEBUG_EVENTS, "Motion enter at grid %s\n", pos.to_string()); m_mouse_cursor_over_widget = TRUE; m_mouse_last_position = pos; @@ -7098,14 +7178,23 @@ Terminal::widget_mouse_enter(vte::platform::MouseEvent const& event) void Terminal::widget_mouse_leave(vte::platform::MouseEvent const& event) { +#if VTE_GTK == 3 auto pos = view_coords_from_event(event); // FIXMEchpe read event modifiers here + // FIXMEgtk4 or maybe not since there is no event to read them from on gtk4 - _vte_debug_print(VTE_DEBUG_EVENTS, "Leave at %s\n", pos.to_string()); + _vte_debug_print(VTE_DEBUG_EVENTS, "Motion leave at grid %s\n", pos.to_string()); m_mouse_cursor_over_widget = FALSE; m_mouse_last_position = pos; +#elif VTE_GTK == 4 + // FIXMEgtk4 !!! + m_mouse_cursor_over_widget = false; + // keep m_mouse_last_position since the event here has no position +#endif + + // FIXMEchpe: also set m_mouse_scroll_delta to 0 here? hyperlink_hilite_update(); match_hilite_update(); @@ -7193,7 +7282,11 @@ Terminal::apply_font_metrics(int cell_width_unscaled, /* Queue a resize if anything's changed. */ if (resize) { if (widget_realized()) { +#if VTE_GTK == 3 gtk_widget_queue_resize_no_redraw(m_widget); +#elif VTE_GTK == 4 + gtk_widget_queue_resize(m_widget); // FIXMEchpe? +#endif } } /* Emit a signal that the font changed. */ @@ -7306,6 +7399,7 @@ Terminal::set_font_desc(vte::Freeable<PangoFontDescription> font_desc) bool Terminal::update_font_desc() { +#if VTE_GTK == 3 auto desc = vte::Freeable<PangoFontDescription>{}; auto context = gtk_widget_get_style_context(m_widget); @@ -7315,6 +7409,16 @@ Terminal::update_font_desc() &vte::get_freeable(desc), nullptr); gtk_style_context_restore(context); +#elif VTE_GTK == 4 + // FIXMEgtk4 + // This is how gtktextview does it, but the APIs are private... thanks, gtk4! + // desc = vte::take_freeable + // (gtk_css_style_get_pango_font(gtk_style_context_lookup_style(context)); + + auto context = gtk_widget_get_pango_context(m_widget); + auto context_desc = pango_context_get_font_description(context); + auto desc = vte::take_freeable(pango_font_description_copy(context_desc)); +#endif /* VTE_GTK */ pango_font_description_set_family_static(desc.get(), "monospace"); @@ -7633,7 +7737,11 @@ Terminal::set_size(long columns, _vte_ring_next (m_screen->row_data) - 1)); adjust_adjustments_full(); +#if VTE_GTK == 3 gtk_widget_queue_resize_no_redraw(m_widget); +#elif VTE_GTK == 4 + gtk_widget_queue_resize(m_widget); // FIXMEchpe? +#endif /* Our visible text changed. */ emit_text_modified(); } @@ -7798,11 +7906,9 @@ Terminal::Terminal(vte::platform::Widget* w, } void -Terminal::widget_get_preferred_width(int *minimum_width, - int *natural_width) +Terminal::widget_measure_width(int *minimum_width, + int *natural_width) { - _vte_debug_print(VTE_DEBUG_LIFECYCLE, "vte_terminal_get_preferred_width()\n"); - ensure_font(); refresh_size(); @@ -7825,11 +7931,9 @@ Terminal::widget_get_preferred_width(int *minimum_width, } void -Terminal::widget_get_preferred_height(int *minimum_height, - int *natural_height) +Terminal::widget_measure_height(int *minimum_height, + int *natural_height) { - _vte_debug_print(VTE_DEBUG_LIFECYCLE, "vte_terminal_get_preferred_height()\n"); - ensure_font(); refresh_size(); @@ -7852,7 +7956,17 @@ Terminal::widget_get_preferred_height(int *minimum_height, } void -Terminal::widget_size_allocate(GtkAllocation *allocation) +#if VTE_GTK == 3 +Terminal::widget_size_allocate(int allocation_x, + int allocation_y, + int allocation_width, + int allocation_height, + int allocation_baseline) +#elif VTE_GTK == 4 +Terminal::widget_size_allocate(int allocation_width, + int allocation_height, + int allocation_baseline) +#endif /* VTE_GTK */ { glong width, height; gboolean repaint, update_scrollback; @@ -7860,9 +7974,9 @@ Terminal::widget_size_allocate(GtkAllocation *allocation) _vte_debug_print(VTE_DEBUG_LIFECYCLE, "vte_terminal_size_allocate()\n"); - width = (allocation->width - (m_padding.left + m_padding.right)) / + width = (allocation_width - (m_padding.left + m_padding.right)) / m_cell_width; - height = (allocation->height - (m_padding.top + m_padding.bottom)) / + height = (allocation_height - (m_padding.top + m_padding.bottom)) / m_cell_height; width = MAX(width, 1); height = MAX(height, 1); @@ -7870,19 +7984,21 @@ Terminal::widget_size_allocate(GtkAllocation *allocation) _vte_debug_print(VTE_DEBUG_WIDGET_SIZE, "[Terminal %p] Sizing window to %dx%d (%ldx%ld, padding %d,%d;%d,%d).\n", m_terminal, - allocation->width, allocation->height, + allocation_width, allocation_height, width, height, m_padding.left, m_padding.right, m_padding.top, m_padding.bottom); auto current_allocation = get_allocated_rect(); - repaint = current_allocation.width != allocation->width - || current_allocation.height != allocation->height; - update_scrollback = current_allocation.height != allocation->height; + repaint = current_allocation.width != allocation_width || + current_allocation.height != allocation_height; + update_scrollback = current_allocation.height != allocation_height; - /* Set our allocation to match the structure. */ - gtk_widget_set_allocation(m_widget, allocation); - set_allocated_rect(*allocation); +#if VTE_GTK == 3 + set_allocated_rect({allocation_x, allocation_y, allocation_width, allocation_height}); +#elif VTE_GTK == 4 + set_allocated_rect({0, 0, allocation_width, allocation_height}); +#endif if (width != m_column_count || height != m_row_count @@ -7949,8 +8065,9 @@ Terminal::set_blink_settings(bool blink, int blink_time, int blink_timeout) noexcept { - m_cursor_blink_cycle = blink_time / 2; - m_cursor_blink_timeout = blink_timeout; + m_cursor_blinks = blink; + m_cursor_blink_cycle = std::max(blink_time / 2, VTE_MIN_CURSOR_BLINK_CYCLE); + m_cursor_blink_timeout = std::max(blink_timeout, VTE_MIN_CURSOR_BLINK_TIMEOUT); update_cursor_blinks(); @@ -9222,6 +9339,7 @@ Terminal::paint_im_preedit_string() height, get_color(VTE_DEFAULT_BG), m_background_alpha); } + draw_cells_with_attributes( items, len, m_im_preedit_attrs.get(), @@ -9248,11 +9366,41 @@ Terminal::paint_im_preedit_string() } } +#if VTE_GTK == 3 + +void +Terminal::widget_draw(cairo_t* cr) +{ +#ifdef VTE_DEBUG + _VTE_DEBUG_IF(VTE_DEBUG_LIFECYCLE | VTE_DEBUG_WORK | VTE_DEBUG_UPDATES) do { + auto clip_rect = cairo_rectangle_int_t{}; + if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect)) + break; + + _vte_debug_print(VTE_DEBUG_LIFECYCLE, "vte_terminal_draw()\n"); + _vte_debug_print (VTE_DEBUG_WORK, "+"); + _vte_debug_print (VTE_DEBUG_UPDATES, "Draw (%d,%d)x(%d,%d)\n", + clip_rect.x, clip_rect.y, + clip_rect.width, clip_rect.height); + } while (0); +#endif /* VTE_DEBUG */ + + auto region = vte_cairo_get_clip_region (cr); + if (!region) + return; + + /* Transform to view coordinates */ + cairo_region_translate(region.get(), -m_padding.left, -m_padding.top); + + draw(cr, region.get()); +} + +#endif /* VTE_GTK == 3 */ + void -Terminal::widget_draw(cairo_t *cr) +Terminal::draw(cairo_t* cr, + cairo_region_t const* region) { - cairo_rectangle_int_t clip_rect; - cairo_region_t *region; int allocated_width, allocated_height; int extra_area_for_cursor; bool text_blink_enabled_now; @@ -9261,19 +9409,6 @@ Terminal::widget_draw(cairo_t *cr) #endif gint64 now = 0; - if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect)) - return; - - _vte_debug_print(VTE_DEBUG_LIFECYCLE, "vte_terminal_draw()\n"); - _vte_debug_print (VTE_DEBUG_WORK, "+"); - _vte_debug_print (VTE_DEBUG_UPDATES, "Draw (%d,%d)x(%d,%d)\n", - clip_rect.x, clip_rect.y, - clip_rect.width, clip_rect.height); - - region = vte_cairo_get_clip_region (cr); - if (region == NULL) - return; - allocated_width = get_allocated_width(); allocated_height = get_allocated_height(); @@ -9294,9 +9429,6 @@ Terminal::widget_draw(cairo_t *cr) cairo_translate(cr, m_padding.left, m_padding.top); - /* Transform to view coordinates */ - cairo_region_translate(region, -m_padding.left, -m_padding.top); - #ifdef WITH_SIXEL /* Draw images */ if (m_images_enabled) { @@ -9365,8 +9497,6 @@ Terminal::widget_draw(cairo_t *cr) /* Done with various structures. */ m_draw.set_cairo(nullptr); - cairo_region_destroy (region); - /* If painting encountered any cell with blink attribute, we might need to set up a timer. * Blinking is implemented using a one-shot (not repeating) timer that keeps getting reinstalled * here as long as blinking cells are encountered during (re)painting. This way there's no need @@ -9380,47 +9510,43 @@ Terminal::widget_draw(cairo_t *cr) m_invalidated_all = FALSE; } +#if VTE_GTK == 3 + /* Handle an expose event by painting the exposed area. */ -static cairo_region_t * +static vte::Freeable<cairo_region_t> vte_cairo_get_clip_region (cairo_t *cr) { - cairo_rectangle_list_t *list; - cairo_region_t *region; - int i; - - list = cairo_copy_clip_rectangle_list (cr); + auto list = vte::take_freeable(cairo_copy_clip_rectangle_list(cr)); if (list->status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) { - cairo_rectangle_int_t clip_rect; - cairo_rectangle_list_destroy (list); + auto clip_rect = cairo_rectangle_int_t{}; + if (!gdk_cairo_get_clip_rectangle(cr, &clip_rect)) + return nullptr; - if (!gdk_cairo_get_clip_rectangle (cr, &clip_rect)) - return NULL; - return cairo_region_create_rectangle (&clip_rect); + return vte::take_freeable(cairo_region_create_rectangle(&clip_rect)); } + auto region = vte::take_freeable(cairo_region_create()); + for (auto i = list->num_rectangles - 1; i >= 0; --i) { + auto rect = &list->rectangles[i]; - region = cairo_region_create (); - for (i = list->num_rectangles - 1; i >= 0; --i) { - cairo_rectangle_t *rect = &list->rectangles[i]; cairo_rectangle_int_t clip_rect; - clip_rect.x = floor (rect->x); clip_rect.y = floor (rect->y); clip_rect.width = ceil (rect->x + rect->width) - clip_rect.x; clip_rect.height = ceil (rect->y + rect->height) - clip_rect.y; - if (cairo_region_union_rectangle (region, &clip_rect) != CAIRO_STATUS_SUCCESS) { - cairo_region_destroy (region); - region = NULL; + if (cairo_region_union_rectangle(region.get(), &clip_rect) != CAIRO_STATUS_SUCCESS) { + region.reset(); break; } } - cairo_rectangle_list_destroy (list); return region; } +#endif /* VTE_GTK == 3 */ + bool Terminal::widget_mouse_scroll(vte::platform::ScrollEvent const& event) { @@ -9652,15 +9778,11 @@ Terminal::set_rewrap_on_resize(bool rewrap) void Terminal::update_cursor_blinks() { - bool blink = false; + auto blink = false; switch (decscusr_cursor_blink()) { case CursorBlinkMode::eSYSTEM: - gboolean v; - g_object_get(gtk_widget_get_settings(m_widget), - "gtk-cursor-blink", - &v, nullptr); - blink = v != FALSE; + blink = m_cursor_blinks_system; break; case CursorBlinkMode::eON: blink = true; @@ -10507,6 +10629,7 @@ Terminal::invalidate_dirty_rects_and_process_updates() if (G_UNLIKELY (!m_update_rects->len)) return false; +#if VTE_GTK == 3 auto region = cairo_region_create(); auto n_rects = m_update_rects->len; for (guint i = 0; i < n_rects; i++) { @@ -10524,6 +10647,9 @@ Terminal::invalidate_dirty_rects_and_process_updates() /* and perform the merge with the window visible area */ gtk_widget_queue_draw_region(m_widget, region); cairo_region_destroy (region); +#elif VTE_GTK == 4 + gtk_widget_queue_draw(m_widget); +#endif return true; } @@ -10928,7 +11054,7 @@ Terminal::set_input_enabled (bool enabled) if (m_has_focus) widget()->im_focus_in(); - gtk_style_context_remove_class (context, GTK_STYLE_CLASS_READ_ONLY); + gtk_style_context_remove_class (context, VTE_STYLE_CLASS_READ_ONLY); } else { im_reset(); if (m_has_focus) @@ -10937,7 +11063,7 @@ Terminal::set_input_enabled (bool enabled) disconnect_pty_write(); _vte_byte_array_clear(m_outgoing); - gtk_style_context_add_class (context, GTK_STYLE_CLASS_READ_ONLY); + gtk_style_context_add_class (context, VTE_STYLE_CLASS_READ_ONLY); } return true; diff --git a/src/vte/meson.build b/src/vte/meson.build index 6d672a49..af614545 100644 --- a/src/vte/meson.build +++ b/src/vte/meson.build @@ -16,21 +16,6 @@ vte_inc = include_directories('.') -libvte_common_enum_headers = files( - # These files contain enums to be extracted by glib-mkenums - 'vtedeprecated.h', - 'vteenums.h', -) - -libvte_gtk3_enum_sources = gnome.mkenums( - 'vtetypebuiltins.h', - sources: libvte_common_enum_headers, - c_template: '../vtetypebuiltins.cc.template', - h_template: '../vtetypebuiltins.h.template', - install_header: true, - install_dir: vte_includedir / vte_gtk3_api_path -) - libvte_common_public_headers = files( 'vte.h', 'vtedeprecated.h', @@ -40,31 +25,111 @@ libvte_common_public_headers = files( 'vtepty.h', 'vteregex.h', 'vteterminal.h', + 'vtetypebuiltins.h', ) +libvte_common_enum_headers = files( + # These files contain enums to be extracted by glib-mkenums + 'vtedeprecated.h', + 'vteenums.h', +) + +# Version header + vteversion_conf = configuration_data() vteversion_conf.set('VTE_MAJOR_VERSION', vte_major_version) vteversion_conf.set('VTE_MINOR_VERSION', vte_minor_version) vteversion_conf.set('VTE_MICRO_VERSION', vte_micro_version) -libvte_version_headers = configure_file( +libvte_common_public_headers += configure_file( input: 'vteversion.h.in', output: '@BASENAME@', configuration: vteversion_conf, install: false, ) +# Install headers, and create the type builtin files. +# Note that we cannot use gnome.mkenums() to create the type builtins +# files, since we need to install the generated header for both gtk3 +# and gtk4, and gnome.mkenums does not work with install_header() +# [https://github.com/mesonbuild/meson/issues/1687]. However, neither does +# custom_target() itself. +# so we need to generate differently-named files for gtk3 and gtk4, and +# install them sepearately, with an extra header that includes the right +# one. And since gnome.mkenums() does not allow specifying the output names +# when using templates, we need to use custom_target() for that. +glib_mkenums = find_program('glib-mkenums') + if get_option('gtk3') - libvte_gtk3_public_headers = libvte_common_public_headers + [libvte_version_headers] + + libvte_gtk3_public_headers = libvte_common_public_headers install_headers( libvte_gtk3_public_headers, subdir: vte_gtk3_api_path ) - # BUG! Due to meson bug, this header cannot be installed with the rule above. Instead, - # use the install_header attribute in the mkenums call, and add the header afterwards - # to the list. - libvte_gtk3_public_headers += libvte_gtk3_enum_sources[1] + libvte_gtk3_public_headers += custom_target( + 'vtetypebuiltins-gtk3.h', + command: [ + glib_mkenums, + '--output', '@OUTPUT@', + '--template', meson.current_source_dir() / '..' / 'vtetypebuiltins.h.template', + '@INPUT@', + ], + input: libvte_common_enum_headers, + install: true, + install_dir: vte_includedir / vte_gtk3_api_path, + output: 'vtetypebuiltins-gtk3.h', + ) + + libvte_gtk3_enum_sources = [custom_target( + 'vtetypebuiltins-gtk3.cc', + command: [ + glib_mkenums, + '--output', '@OUTPUT@', + '--template', meson.current_source_dir() / '..' / 'vtetypebuiltins.cc.template', + '@INPUT@', + ], + input: libvte_common_enum_headers, + install: false, + output: 'vtetypebuiltins-gtk3.cc', + ),] +endif + +if get_option('gtk4') + + libvte_gtk4_public_headers = libvte_common_public_headers + + install_headers( + libvte_gtk4_public_headers, + subdir: vte_gtk4_api_path + ) + + libvte_gtk4_public_headers += custom_target( + 'vtetypebuiltins-gtk4.h', + command: [ + glib_mkenums, + '--output', '@OUTPUT@', + '--template', meson.current_source_dir() / '..' / 'vtetypebuiltins.h.template', + '@INPUT@', + ], + input: libvte_common_enum_headers, + install: true, + install_dir: vte_includedir / vte_gtk4_api_path, + output: 'vtetypebuiltins-gtk4.h', + ) + libvte_gtk4_enum_sources = [custom_target( + 'vtetypebuiltins-gtk4.cc', + command: [ + glib_mkenums, + '--output', '@OUTPUT@', + '--template', meson.current_source_dir() / '..' / 'vtetypebuiltins.cc.template', + '@INPUT@', + ], + input: libvte_common_enum_headers, + install: false, + output: 'vtetypebuiltins-gtk4.cc', + ),] endif diff --git a/src/vte/vte.h b/src/vte/vte.h index d4a6ff58..c9fa1fe6 100644 --- a/src/vte/vte.h +++ b/src/vte/vte.h @@ -18,9 +18,13 @@ #pragma once #include <glib.h> +#include <gtk/gtk.h> #define __VTE_VTE_H_INSIDE__ 1 +/* This must always be included first */ +#include "vtemacros.h" + #include "vteenums.h" #include "vteglobals.h" #include "vtepty.h" diff --git a/src/vte/vtedeprecated.h b/src/vte/vtedeprecated.h index cbc1b57c..fd4913eb 100644 --- a/src/vte/vtedeprecated.h +++ b/src/vte/vtedeprecated.h @@ -33,11 +33,15 @@ G_BEGIN_DECLS +#if _VTE_GTK == 3 + _VTE_DEPRECATED _VTE_PUBLIC int vte_terminal_match_add_gregex(VteTerminal *terminal, GRegex *gregex, - GRegexMatchFlags gflags) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2); + GRegexMatchFlags gflags) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2); + +#endif /* _VTE_GTK == 3 */ _VTE_DEPRECATED _VTE_PUBLIC @@ -45,11 +49,13 @@ void vte_terminal_match_set_cursor(VteTerminal *terminal, int tag, GdkCursor *cursor) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); +#if _VTE_GTK == 3 _VTE_DEPRECATED _VTE_PUBLIC void vte_terminal_match_set_cursor_type(VteTerminal *terminal, int tag, GdkCursorType cursor_type) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); +#endif _VTE_DEPRECATED _VTE_PUBLIC @@ -57,6 +63,8 @@ char *vte_terminal_match_check(VteTerminal *terminal, glong column, glong row, int *tag) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) G_GNUC_MALLOC; +#if _VTE_GTK == 3 + _VTE_DEPRECATED _VTE_PUBLIC gboolean vte_terminal_event_check_gregex_simple(VteTerminal *terminal, @@ -64,7 +72,7 @@ gboolean vte_terminal_event_check_gregex_simple(VteTerminal *terminal, GRegex **regexes, gsize n_regexes, GRegexMatchFlags match_flags, - char **matches) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2); + char **matches) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2); _VTE_DEPRECATED _VTE_PUBLIC @@ -76,6 +84,8 @@ _VTE_DEPRECATED _VTE_PUBLIC GRegex *vte_terminal_search_get_gregex (VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); +#endif /* _VTE_GTK == 3 */ + _VTE_DEPRECATED _VTE_PUBLIC gboolean vte_terminal_spawn_sync(VteTerminal *terminal, @@ -88,7 +98,7 @@ gboolean vte_terminal_spawn_sync(VteTerminal *terminal, gpointer child_setup_data, GPid *child_pid /* out */, GCancellable *cancellable, - GError **error) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(4); + GError **error) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 4); _VTE_DEPRECATED _VTE_PUBLIC @@ -98,17 +108,21 @@ _VTE_DEPRECATED _VTE_PUBLIC void vte_terminal_copy_clipboard(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); +#if _VTE_GTK == 3 + _VTE_DEPRECATED _VTE_PUBLIC void vte_terminal_get_geometry_hints(VteTerminal *terminal, GdkGeometry *hints, int min_rows, - int min_columns) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2); + int min_columns) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2); _VTE_DEPRECATED _VTE_PUBLIC void vte_terminal_set_geometry_hints_for_window(VteTerminal *terminal, - GtkWindow *window) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2); + GtkWindow *window) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2); + +#endif /* _VTE_GTK == 3 */ _VTE_DEPRECATED _VTE_PUBLIC diff --git a/src/vte/vtemacros.h b/src/vte/vtemacros.h index 30ba7039..c1300998 100644 --- a/src/vte/vtemacros.h +++ b/src/vte/vtemacros.h @@ -21,6 +21,18 @@ #error "Only <vte/vte.h> can be included directly." #endif +#include <gtk/gtk.h> + +#if GTK_CHECK_VERSION(4,0,0) +#define _VTE_GTK 4 +#elif GTK_CHECK_VERSION(3,90,0) +#error gtk+ version not supported +#elif GTK_CHECK_VERSION(3,0,0) +#define _VTE_GTK 3 +#else +#error gtk+ version unknown +#endif + #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 6) #define _VTE_GNUC_PACKED __attribute__((__packed__)) #else @@ -28,12 +40,12 @@ #endif /* !__GNUC__ */ #ifdef VTE_COMPILATION -#define _VTE_GNUC_NONNULL(position) +#define _VTE_GNUC_NONNULL(...) #else -#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 2) -#define _VTE_GNUC_NONNULL(position) __attribute__((__nonnull__(position))) +#if defined(__GNUC__) +#define _VTE_GNUC_NONNULL(...) __attribute__((__nonnull__(__VA_ARGS__))) #else -#define _VTE_GNUC_NONNULL(position) +#define _VTE_GNUC_NONNULL(...) #endif #endif diff --git a/src/vte/vtepty.h b/src/vte/vtepty.h index 524e91d6..f6348cd7 100644 --- a/src/vte/vtepty.h +++ b/src/vte/vtepty.h @@ -91,8 +91,6 @@ gboolean vte_pty_set_utf8 (VtePty *pty, gboolean utf8, GError **error) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); -G_DEFINE_AUTOPTR_CLEANUP_FUNC(VtePty, g_object_unref) - _VTE_PUBLIC void vte_pty_spawn_async(VtePty *pty, const char *working_directory, @@ -105,7 +103,7 @@ void vte_pty_spawn_async(VtePty *pty, int timeout, GCancellable *cancellable, GAsyncReadyCallback callback, - gpointer user_data) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(3); + gpointer user_data) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 3); _VTE_PUBLIC void vte_pty_spawn_with_fds_async(VtePty *pty, @@ -123,12 +121,14 @@ void vte_pty_spawn_with_fds_async(VtePty *pty, int timeout, GCancellable *cancellable, GAsyncReadyCallback callback, - gpointer user_data) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(3); + gpointer user_data) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 3); _VTE_PUBLIC gboolean vte_pty_spawn_finish(VtePty *pty, GAsyncResult *result, GPid *child_pid /* out */, - GError **error) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2); + GError **error) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(VtePty, g_object_unref) G_END_DECLS diff --git a/src/vte/vteregex.h b/src/vte/vteregex.h index 626ae87c..6eb95343 100644 --- a/src/vte/vteregex.h +++ b/src/vte/vteregex.h @@ -71,7 +71,7 @@ char *vte_regex_substitute(VteRegex *regex, const char *subject, const char *replacement, guint32 flags, - GError **error) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2) _VTE_GNUC_NONNULL(3) G_GNUC_MALLOC; + GError **error) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2, 3) G_GNUC_MALLOC; G_DEFINE_AUTOPTR_CLEANUP_FUNC(VteRegex, vte_regex_unref) diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h index e705ece6..736b3e77 100644 --- a/src/vte/vteterminal.h +++ b/src/vte/vteterminal.h @@ -50,8 +50,10 @@ typedef struct _VteCharAttributes VteCharAttributes; */ struct _VteTerminal { GtkWidget widget; +#if _VTE_GTK == 3 /*< private >*/ gpointer *_unused_padding[1]; /* FIXMEchpe: remove this field on the next ABI break */ +#endif }; /** @@ -105,6 +107,7 @@ struct _VteTerminalClass { /* Padding for future expansion. */ gpointer padding[16]; +// FIXMEgtk4 use class private data instead VteTerminalClassPrivate *priv; }; @@ -156,7 +159,7 @@ void vte_terminal_spawn_async(VteTerminal *terminal, int timeout, GCancellable *cancellable, VteTerminalSpawnAsyncCallback callback, - gpointer user_data) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(4); + gpointer user_data) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 4); _VTE_PUBLIC void vte_terminal_spawn_with_fds_async(VteTerminal* terminal, @@ -175,7 +178,7 @@ void vte_terminal_spawn_with_fds_async(VteTerminal* terminal, int timeout, GCancellable* cancellable, VteTerminalSpawnAsyncCallback callback, - gpointer user_data) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(4); + gpointer user_data) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 4); /* Send data to the terminal to display, or to the terminal's forked command * to handle in some way. If it's 'cat', they should be the same. */ @@ -268,10 +271,10 @@ void vte_terminal_set_color_bold(VteTerminal *terminal, const GdkRGBA *bold) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); _VTE_PUBLIC void vte_terminal_set_color_foreground(VteTerminal *terminal, - const GdkRGBA *foreground) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2); + const GdkRGBA *foreground) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2); _VTE_PUBLIC void vte_terminal_set_color_background(VteTerminal *terminal, - const GdkRGBA *background) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2); + const GdkRGBA *background) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2); _VTE_PUBLIC void vte_terminal_set_color_cursor(VteTerminal *terminal, const GdkRGBA *cursor_background) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); @@ -395,21 +398,25 @@ void vte_terminal_get_cursor_position(VteTerminal *terminal, glong *column, glong *row) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); +#if _VTE_GTK == 3 + _VTE_PUBLIC char *vte_terminal_hyperlink_check_event(VteTerminal *terminal, - GdkEvent *event) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2) G_GNUC_MALLOC; + GdkEvent *event) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2) G_GNUC_MALLOC; + +#endif /* _VTE_GTK */ /* Add a matching expression, returning the tag the widget assigns to that * expression. */ _VTE_PUBLIC int vte_terminal_match_add_regex(VteTerminal *terminal, VteRegex *regex, - guint32 flags) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2); + guint32 flags) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2); /* Set the cursor to be used when the pointer is over a given match. */ _VTE_PUBLIC void vte_terminal_match_set_cursor_name(VteTerminal *terminal, int tag, - const char *cursor_name) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(3); + const char *cursor_name) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 3); _VTE_PUBLIC void vte_terminal_match_remove(VteTerminal *terminal, int tag) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); @@ -419,24 +426,29 @@ void vte_terminal_match_remove_all(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE /* Check if a given cell on the screen contains part of a matched string. If * it does, return the string, and store the match tag in the optional tag * argument. */ +#if _VTE_GTK == 3 + _VTE_PUBLIC char *vte_terminal_match_check_event(VteTerminal *terminal, GdkEvent *event, - int *tag) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2) G_GNUC_MALLOC; + int *tag) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2) G_GNUC_MALLOC; + _VTE_PUBLIC char **vte_terminal_event_check_regex_array(VteTerminal *terminal, GdkEvent *event, VteRegex **regexes, gsize n_regexes, guint32 match_flags, - gsize *n_matches) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2) G_GNUC_MALLOC; + gsize *n_matches) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2) G_GNUC_MALLOC; _VTE_PUBLIC gboolean vte_terminal_event_check_regex_simple(VteTerminal *terminal, GdkEvent *event, VteRegex **regexes, gsize n_regexes, guint32 match_flags, - char **matches) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2); + char **matches) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2); + +#endif /* _VTE_GTK */ _VTE_PUBLIC void vte_terminal_search_set_regex (VteTerminal *terminal, @@ -492,12 +504,17 @@ _VTE_PUBLIC gboolean vte_terminal_get_input_enabled (VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); /* rarely useful functions */ + +#if _VTE_GTK == 3 + _VTE_PUBLIC void vte_terminal_set_clear_background(VteTerminal* terminal, gboolean setting) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); _VTE_PUBLIC void vte_terminal_get_color_background_for_draw(VteTerminal* terminal, - GdkRGBA* color) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2); + GdkRGBA* color) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2); + +#endif /* _VTE_GTK == 3 */ /* Writing contents out */ _VTE_PUBLIC @@ -505,7 +522,7 @@ gboolean vte_terminal_write_contents_sync (VteTerminal *terminal, GOutputStream *stream, VteWriteFlags flags, GCancellable *cancellable, - GError **error) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2); + GError **error) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1, 2); /* Images */ diff --git a/src/vte/vtetypebuiltins.h b/src/vte/vtetypebuiltins.h new file mode 100644 index 00000000..8678cdae --- /dev/null +++ b/src/vte/vtetypebuiltins.h @@ -0,0 +1,28 @@ +/* + * Copyright © 2021 Christian Persch + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#if !defined (__VTE_VTE_H_INSIDE__) && !defined (VTE_COMPILATION) +#error "Only <vte/vte.h> can be included directly." +#endif + +#if _VTE_GTK == 3 +#include "vtetypebuiltins-gtk3.h" +#elif _VTE_GTK == 4 +#include "vtetypebuiltins-gtk4.h" +#endif diff --git a/src/vteaccess.h b/src/vteaccess.h index 4362ede4..31f21601 100644 --- a/src/vteaccess.h +++ b/src/vteaccess.h @@ -15,9 +15,7 @@ * along with this library. If not, see <https://www.gnu.org/licenses/>. */ -#ifndef vte_vteaccess_h_included -#define vte_vteaccess_h_included - +#pragma once #include <glib.h> #include <gtk/gtk.h> @@ -51,5 +49,3 @@ struct _VteTerminalAccessibleClass { GType _vte_terminal_accessible_get_type(void); G_END_DECLS - -#endif diff --git a/src/vtedefines.hh b/src/vtedefines.hh index b1720735..16f6ae78 100644 --- a/src/vtedefines.hh +++ b/src/vtedefines.hh @@ -148,3 +148,6 @@ #define VTE_SIXEL_MAX_WIDTH (2048) #define VTE_SIXEL_MAX_HEIGHT (2052) #define VTE_SIXEL_NUM_COLOR_REGISTERS (1024) + +#define VTE_MIN_CURSOR_BLINK_CYCLE (50 /* ms */) +#define VTE_MIN_CURSOR_BLINK_TIMEOUT (50 /* ms */) diff --git a/src/vtegtk.cc b/src/vtegtk.cc index a1be90df..cee355a1 100644 --- a/src/vtegtk.cc +++ b/src/vtegtk.cc @@ -63,8 +63,12 @@ #include "vteregexinternal.hh" #ifdef WITH_A11Y +#if VTE_GTK == 3 #include "vteaccess.h" -#endif +#else +#undef WITH_A11Y +#endif /* VTE_GTK == 3 */ +#endif /* WITH_A11Y */ #ifdef WITH_ICU #include "icu-glue.hh" @@ -79,6 +83,19 @@ struct _VteTerminalClassPrivate { GtkStyleProvider *style_provider; }; +#if VTE_GTK == 4 + +static void +style_provider_parsing_error_cb(GtkCssProvider* provider, + void* section, + GError* error) +{ + g_assert_no_error(error); +} + +#endif + + class VteTerminalPrivate { public: VteTerminalPrivate(VteTerminal* terminal) @@ -217,7 +234,6 @@ vte_terminal_set_hscroll_policy(VteTerminal *terminal, try { WIDGET(terminal)->set_hscroll_policy(policy); - gtk_widget_queue_resize_no_redraw (GTK_WIDGET (terminal)); } catch (...) { @@ -230,7 +246,6 @@ vte_terminal_set_vscroll_policy(VteTerminal *terminal, try { WIDGET(terminal)->set_vscroll_policy(policy); - gtk_widget_queue_resize_no_redraw (GTK_WIDGET (terminal)); } catch (...) { @@ -260,6 +275,8 @@ catch (...) vte::log_exception(); } +#if VTE_GTK == 3 + static void vte_terminal_style_updated (GtkWidget *widget) noexcept try @@ -298,7 +315,7 @@ try } } - return WIDGET(terminal)->key_press(event); + return WIDGET(terminal)->event_key_press(event); } catch (...) { @@ -312,7 +329,7 @@ vte_terminal_key_release(GtkWidget *widget, try { VteTerminal *terminal = VTE_TERMINAL(widget); - return WIDGET(terminal)->key_release(event); + return WIDGET(terminal)->event_key_release(event); } catch (...) { @@ -326,7 +343,7 @@ vte_terminal_motion_notify(GtkWidget *widget, try { VteTerminal *terminal = VTE_TERMINAL(widget); - return WIDGET(terminal)->motion_notify(event); + return WIDGET(terminal)->event_motion_notify(event); } catch (...) { @@ -340,7 +357,7 @@ vte_terminal_button_press(GtkWidget *widget, try { VteTerminal *terminal = VTE_TERMINAL(widget); - return WIDGET(terminal)->button_press(event); + return WIDGET(terminal)->event_button_press(event); } catch (...) { @@ -354,7 +371,7 @@ vte_terminal_button_release(GtkWidget *widget, try { VteTerminal *terminal = VTE_TERMINAL(widget); - return WIDGET(terminal)->button_release(event); + return WIDGET(terminal)->event_button_release(event); } catch (...) { @@ -368,7 +385,7 @@ vte_terminal_scroll(GtkWidget *widget, try { auto terminal = VTE_TERMINAL(widget); - return WIDGET(terminal)->scroll(event); + return WIDGET(terminal)->event_scroll(event); } catch (...) { @@ -382,7 +399,7 @@ vte_terminal_focus_in(GtkWidget *widget, try { VteTerminal *terminal = VTE_TERMINAL(widget); - WIDGET(terminal)->focus_in(event); + WIDGET(terminal)->event_focus_in(event); return FALSE; } catch (...) @@ -397,7 +414,7 @@ vte_terminal_focus_out(GtkWidget *widget, try { VteTerminal *terminal = VTE_TERMINAL(widget); - WIDGET(terminal)->focus_out(event); + WIDGET(terminal)->event_focus_out(event); return FALSE; } catch (...) @@ -418,7 +435,7 @@ try ret = GTK_WIDGET_CLASS (vte_terminal_parent_class)->enter_notify_event (widget, event); } - WIDGET(terminal)->enter(event); + WIDGET(terminal)->event_enter(event); return ret; } @@ -440,7 +457,7 @@ try ret = GTK_WIDGET_CLASS (vte_terminal_parent_class)->leave_notify_event (widget, event); } - WIDGET(terminal)->leave(event); + WIDGET(terminal)->event_leave(event); return ret; } @@ -478,33 +495,7 @@ catch (...) vte::log_exception(); } -static void -vte_terminal_size_allocate(GtkWidget *widget, - GtkAllocation *allocation) noexcept -try -{ - VteTerminal *terminal = VTE_TERMINAL(widget); - WIDGET(terminal)->size_allocate(allocation); -} -catch (...) -{ - vte::log_exception(); -} - -static gboolean -vte_terminal_draw(GtkWidget *widget, - cairo_t *cr) noexcept -try -{ - VteTerminal *terminal = VTE_TERMINAL (widget); - WIDGET(terminal)->draw(cr); - return FALSE; -} -catch (...) -{ - vte::log_exception(); - return false; -} +#endif /* VTE_GTK == 3 */ static void vte_terminal_realize(GtkWidget *widget) noexcept @@ -569,16 +560,105 @@ vte_terminal_unmap(GtkWidget *widget) noexcept } static void -vte_terminal_screen_changed (GtkWidget *widget, - GdkScreen *previous_screen) noexcept +vte_terminal_state_flags_changed(GtkWidget* widget, + GtkStateFlags old_flags) noexcept try { - VteTerminal *terminal = VTE_TERMINAL (widget); + GTK_WIDGET_CLASS(vte_terminal_parent_class)->state_flags_changed(widget, old_flags); - if (GTK_WIDGET_CLASS (vte_terminal_parent_class)->screen_changed) { - GTK_WIDGET_CLASS (vte_terminal_parent_class)->screen_changed (widget, previous_screen); - } + auto terminal = VTE_TERMINAL(widget); + WIDGET(terminal)->state_flags_changed(old_flags); +} +catch (...) +{ + vte::log_exception(); +} + +static void +vte_terminal_direction_changed(GtkWidget* widget, + GtkTextDirection old_direction) noexcept +try +{ + auto const parent_class = GTK_WIDGET_CLASS(vte_terminal_parent_class); + if (parent_class->direction_changed) + parent_class->direction_changed(widget, old_direction); + + auto terminal = VTE_TERMINAL(widget); + WIDGET(terminal)->direction_changed(old_direction); +} +catch (...) +{ + vte::log_exception(); +} + +static GtkSizeRequestMode +vte_terminal_get_request_mode(GtkWidget* widget) noexcept +{ + return GTK_SIZE_REQUEST_CONSTANT_SIZE; +} + +static gboolean +vte_terminal_query_tooltip(GtkWidget* widget, + int x, + int y, + gboolean keyboard, + GtkTooltip* tooltip) noexcept +try +{ + auto const parent_class = GTK_WIDGET_CLASS(vte_terminal_parent_class); + if (parent_class->query_tooltip(widget, x, y, keyboard, tooltip)) + return true; + + auto terminal = VTE_TERMINAL(widget); + return WIDGET(terminal)->query_tooltip(x, y, keyboard, tooltip); +} +catch (...) +{ + vte::log_exception(); + return false; +} + + +#if VTE_GTK == 3 + +static void +vte_terminal_size_allocate(GtkWidget* widget, + GtkAllocation* allocation) noexcept +try +{ + auto terminal = VTE_TERMINAL(widget); + WIDGET(terminal)->size_allocate(allocation); +} +catch (...) +{ + vte::log_exception(); +} + +static gboolean +vte_terminal_draw(GtkWidget* widget, + cairo_t* cr) noexcept +try +{ + auto terminal = VTE_TERMINAL(widget); + WIDGET(terminal)->draw(cr); + return FALSE; +} +catch (...) +{ + vte::log_exception(); + return false; +} +static void +vte_terminal_screen_changed(GtkWidget* widget, + GdkScreen* previous_screen) noexcept +try +{ + auto const parent_class = GTK_WIDGET_CLASS(vte_terminal_parent_class); + if (parent_class->screen_changed) + parent_class->screen_changed(widget, previous_screen); + + auto terminal = VTE_TERMINAL(widget); WIDGET(terminal)->screen_changed(previous_screen); } catch (...) @@ -586,6 +666,156 @@ catch (...) vte::log_exception(); } +#endif /* VTE_GTK == 3 */ + +#if VTE_GTK == 4 + +static void +vte_terminal_size_allocate(GtkWidget *widget, + int width, + int height, + int baseline) noexcept +try +{ + GTK_WIDGET_CLASS(vte_terminal_parent_class)->size_allocate(widget, width, height, baseline); + + auto terminal = VTE_TERMINAL(widget); + WIDGET(terminal)->size_allocate(width, height, baseline); +} +catch (...) +{ + vte::log_exception(); +} + +static void +vte_terminal_root(GtkWidget *widget) noexcept +try +{ + GTK_WIDGET_CLASS(vte_terminal_parent_class)->root(widget); + + auto terminal = VTE_TERMINAL(widget); + WIDGET(terminal)->root(); +} +catch (...) +{ + vte::log_exception(); +} + +static void +vte_terminal_unroot(GtkWidget *widget) noexcept +{ + _vte_debug_print(VTE_DEBUG_LIFECYCLE, "vte_terminal_unroot()\n"); + + auto terminal = VTE_TERMINAL(widget); + WIDGET(terminal)->unroot(); + + GTK_WIDGET_CLASS(vte_terminal_parent_class)->unroot(widget); +} + +static void +vte_terminal_measure(GtkWidget* widget, + GtkOrientation orientation, + int for_size, + int* minimum, + int* natural, + int* minimum_baseline, + int* natural_baseline) noexcept +try +{ + auto terminal = VTE_TERMINAL(widget); + WIDGET(terminal)->measure(orientation, for_size, + minimum, natural, + minimum_baseline, natural_baseline); +} +catch (...) +{ + vte::log_exception(); +} + +static void +vte_terminal_compute_expand(GtkWidget* widget, + gboolean* hexpand, + gboolean* vexpand) noexcept +try +{ + auto terminal = VTE_TERMINAL(widget); + auto [h, v] = WIDGET(terminal)->compute_expand(); + *hexpand = h; + *vexpand = v; +} +catch (...) +{ + vte::log_exception(); + *hexpand = *vexpand = false; +} + +static void +vte_terminal_css_changed(GtkWidget* widget, + GtkCssStyleChange* change) noexcept +try +{ + GTK_WIDGET_CLASS(vte_terminal_parent_class)->css_changed(widget, change); + auto terminal = VTE_TERMINAL(widget); + WIDGET(terminal)->css_changed(change); +} +catch (...) +{ + vte::log_exception(); +} + +static void +vte_terminal_system_setting_changed(GtkWidget* widget, + GtkSystemSetting setting) noexcept +try +{ + GTK_WIDGET_CLASS(vte_terminal_parent_class)->system_setting_changed(widget, setting); + auto terminal = VTE_TERMINAL(widget); + WIDGET(terminal)->system_setting_changed(setting); +} +catch (...) +{ + vte::log_exception(); +} + +static void +vte_terminal_snapshot(GtkWidget* widget, + GtkSnapshot* snapshot_object) noexcept +try +{ + GTK_WIDGET_CLASS(vte_terminal_parent_class)->snapshot(widget, snapshot_object); + auto terminal = VTE_TERMINAL(widget); + WIDGET(terminal)->snapshot(snapshot_object); +} +catch (...) +{ + vte::log_exception(); +} + +static gboolean +vte_terminal_contains(GtkWidget* widget, + double x, + double y) noexcept +try +{ + auto terminal = VTE_TERMINAL(widget); + if (WIDGET(terminal)->contains(x, y)) + return true; + + auto const parent_class = GTK_WIDGET_CLASS(vte_terminal_parent_class); + if (parent_class->contains && + parent_class->contains(widget, x, y)) + return true; + + return false; +} +catch (...) +{ + vte::log_exception(); + return false; +} + +#endif /* VTE_GTK == 4 */ + static void vte_terminal_constructed (GObject *object) noexcept try @@ -615,7 +845,9 @@ try VTE_TERMINAL_GET_CLASS (terminal)->priv->style_provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); +#if VTE_GTK == 3 gtk_widget_set_has_window(&terminal->widget, FALSE); +#endif place = vte_terminal_get_instance_private(terminal); new (place) VteTerminalPrivate{terminal}; @@ -916,10 +1148,6 @@ catch (...) static void vte_terminal_class_init(VteTerminalClass *klass) { - GObjectClass *gobject_class; - GtkWidgetClass *widget_class; - GtkBindingSet *binding_set; - #ifdef VTE_DEBUG { _vte_debug_init(); @@ -943,13 +1171,15 @@ vte_terminal_class_init(VteTerminalClass *klass) } #endif +#if VTE_GTK == 3 _VTE_DEBUG_IF (VTE_DEBUG_UPDATES) gdk_window_set_debug_updates(TRUE); +#endif bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); - gobject_class = G_OBJECT_CLASS(klass); - widget_class = GTK_WIDGET_CLASS(klass); + auto gobject_class = G_OBJECT_CLASS(klass); + auto widget_class = GTK_WIDGET_CLASS(klass); /* Override some of the default handlers. */ gobject_class->constructed = vte_terminal_constructed; @@ -957,12 +1187,21 @@ vte_terminal_class_init(VteTerminalClass *klass) gobject_class->finalize = vte_terminal_finalize; gobject_class->get_property = vte_terminal_get_property; gobject_class->set_property = vte_terminal_set_property; + widget_class->realize = vte_terminal_realize; widget_class->unrealize = vte_terminal_unrealize; widget_class->map = vte_terminal_map; widget_class->unmap = vte_terminal_unmap; - widget_class->scroll_event = vte_terminal_scroll; + + widget_class->size_allocate = vte_terminal_size_allocate; + widget_class->state_flags_changed = vte_terminal_state_flags_changed; + widget_class->direction_changed = vte_terminal_direction_changed; + widget_class->get_request_mode = vte_terminal_get_request_mode; + widget_class->query_tooltip = vte_terminal_query_tooltip; + +#if VTE_GTK == 3 widget_class->draw = vte_terminal_draw; + widget_class->scroll_event = vte_terminal_scroll; widget_class->key_press_event = vte_terminal_key_press; widget_class->key_release_event = vte_terminal_key_release; widget_class->button_press_event = vte_terminal_button_press; @@ -975,8 +1214,19 @@ vte_terminal_class_init(VteTerminalClass *klass) widget_class->style_updated = vte_terminal_style_updated; widget_class->get_preferred_width = vte_terminal_get_preferred_width; widget_class->get_preferred_height = vte_terminal_get_preferred_height; - widget_class->size_allocate = vte_terminal_size_allocate; widget_class->screen_changed = vte_terminal_screen_changed; +#endif + +#if VTE_GTK == 4 + widget_class->root = vte_terminal_root; + widget_class->unroot = vte_terminal_unroot; + widget_class->measure = vte_terminal_measure; + widget_class->compute_expand = vte_terminal_compute_expand; + widget_class->css_changed = vte_terminal_css_changed; + widget_class->system_setting_changed = vte_terminal_system_setting_changed; + widget_class->snapshot = vte_terminal_snapshot; + widget_class->contains = vte_terminal_contains; +#endif gtk_widget_class_set_css_name(widget_class, VTE_TERMINAL_CSS_NAME); @@ -2077,23 +2327,33 @@ vte_terminal_class_init(VteTerminalClass *klass) g_object_class_install_properties(gobject_class, LAST_PROP, pspecs); +#if VTE_GTK == 3 /* Disable GtkWidget's keybindings except for Shift-F10 and MenuKey * which pop up the context menu. */ - binding_set = gtk_binding_set_by_class(vte_terminal_parent_class); + auto const binding_set = gtk_binding_set_by_class(vte_terminal_parent_class); gtk_binding_entry_skip(binding_set, GDK_KEY_F1, GDK_CONTROL_MASK); gtk_binding_entry_skip(binding_set, GDK_KEY_F1, GDK_SHIFT_MASK); gtk_binding_entry_skip(binding_set, GDK_KEY_KP_F1, GDK_CONTROL_MASK); gtk_binding_entry_skip(binding_set, GDK_KEY_KP_F1, GDK_SHIFT_MASK); +#endif /* VTE_GTK == 3 */ process_timer = g_timer_new(); klass->priv = G_TYPE_CLASS_GET_PRIVATE (klass, VTE_TYPE_TERMINAL, VteTerminalClassPrivate); klass->priv->style_provider = GTK_STYLE_PROVIDER (gtk_css_provider_new ()); +#if VTE_GTK == 3 + auto err = vte::glib::Error{}; +#elif VTE_GTK == 4 + g_signal_connect(klass->priv->style_provider, "parsing-error", + G_CALLBACK(style_provider_parsing_error_cb), nullptr); +#endif gtk_css_provider_load_from_data (GTK_CSS_PROVIDER (klass->priv->style_provider), "VteTerminal, " VTE_TERMINAL_CSS_NAME " {\n" +#if VTE_GTK == 3 "padding: 1px 1px 1px 1px;\n" +#endif #if GTK_CHECK_VERSION (3, 24, 22) "background-color: @text_view_bg;\n" #else @@ -2101,12 +2361,21 @@ vte_terminal_class_init(VteTerminalClass *klass) #endif "color: @theme_text_color;\n" "}\n", - -1, NULL); + -1 +#if VTE_GTK == 3 + , NULL +#endif + ); +#if VTE_GTK == 3 + err.assert_no_error(); +#endif +#if VTE_GTK == 3 #ifdef WITH_A11Y /* a11y */ gtk_widget_class_set_accessible_type(widget_class, VTE_TYPE_TERMINAL_ACCESSIBLE); #endif +#endif } static gboolean @@ -2504,6 +2773,8 @@ catch (...) vte::log_exception(); } +#if VTE_GTK == 3 + /** * vte_terminal_match_add_gregex: * @terminal: a #VteTerminal @@ -2524,6 +2795,8 @@ vte_terminal_match_add_gregex(VteTerminal *terminal, return -1; } +#endif /* VTE_GTK == 3 */ + /** * vte_terminal_match_add_regex: * @terminal: a #VteTerminal @@ -2600,6 +2873,8 @@ catch (...) return nullptr; } +#if VTE_GTK == 3 + /** * vte_terminal_match_check_event: * @terminal: a #VteTerminal @@ -2768,6 +3043,10 @@ catch (...) return false; } +#endif /* VTE_GTK */ + +#if VTE_GTK == 3 + /** * vte_terminal_event_check_gregex_simple: * @terminal: a #VteTerminal @@ -2795,6 +3074,8 @@ vte_terminal_event_check_gregex_simple(VteTerminal *terminal, return FALSE; } +#endif /* VTE_GTK == 3 */ + /** * vte_terminal_match_set_cursor: * @terminal: a #VteTerminal @@ -2823,6 +3104,8 @@ catch (...) vte::log_exception(); } +#if VTE_GTK == 3 + /** * vte_terminal_match_set_cursor_type: * @terminal: a #VteTerminal @@ -2849,6 +3132,7 @@ catch (...) { vte::log_exception(); } +#endif /* VTE_GTK == 3 */ /** * vte_terminal_match_set_cursor_name: @@ -3011,6 +3295,8 @@ catch (...) return nullptr; } +#if VTE_GTK == 3 + /** * vte_terminal_search_set_gregex: * @terminal: a #VteTerminal @@ -3044,6 +3330,8 @@ vte_terminal_search_get_gregex (VteTerminal *terminal) noexcept return nullptr; } +#endif /* VTE_GTK == 3 */ + /** * vte_terminal_search_set_wrap_around: * @terminal: a #VteTerminal @@ -5046,6 +5334,8 @@ catch (...) vte::log_exception(); } +#if VTE_GTK == 3 + /* Just some arbitrary minimum values */ #define MIN_COLUMNS (16) #define MIN_ROWS (2) @@ -5136,6 +5426,8 @@ vte_terminal_set_geometry_hints_for_window(VteTerminal *terminal, GDK_HINT_BASE_SIZE)); } +#endif /* VTE_GTK == 3 */ + /** * vte_terminal_get_has_selection: * @terminal: a #VteTerminal @@ -5698,6 +5990,8 @@ catch (...) return vte::glib::set_error_from_exception(error); } +#if VTE_GTK == 3 + /** * vte_terminal_set_clear_background: * @terminal: a #VteTerminal @@ -5764,6 +6058,8 @@ catch (...) *color = {0., 0., 0., 1.}; } +#endif /* VTE_GTK == 3 */ + /** * vte_terminal_set_enable_sixel: * @terminal: a #VteTerminal diff --git a/src/vteinternal.hh b/src/vteinternal.hh index 41766c0e..a6ad315b 100644 --- a/src/vteinternal.hh +++ b/src/vteinternal.hh @@ -89,9 +89,14 @@ namespace platform { * Holds a platform cursor. This is either a named cursor (string), * a reference to a GdkCursor*, or a cursor type. */ +#if VTE_GTK == 3 using Cursor = std::variant<std::string, vte::glib::RefPtr<GdkCursor>, GdkCursorType>; +#elif VTE_GTK == 4 +using Cursor = std::variant<std::string, + vte::glib::RefPtr<GdkCursor>>; +#endif } // namespace platform } // namespace vte @@ -427,9 +432,10 @@ public: "cursor-blink-timer"}; CursorBlinkMode m_cursor_blink_mode{CursorBlinkMode::eSYSTEM}; bool m_cursor_blink_state{false}; - bool m_cursor_blinks{false}; /* whether the cursor is actually blinking */ - gint m_cursor_blink_cycle; /* gtk-cursor-blink-time / 2 */ - int m_cursor_blink_timeout{500}; /* gtk-cursor-blink-timeout */ + bool m_cursor_blinks{false}; /* whether the cursor is actually blinking */ + bool m_cursor_blinks_system{true}; /* gtk-cursor-blink */ + gint m_cursor_blink_cycle{1000}; /* gtk-cursor-blink-time / 2 */ + int m_cursor_blink_timeout{500}; /* gtk-cursor-blink-timeout */ gint64 m_cursor_blink_time; /* how long the cursor has been blinking yet */ bool m_has_focus{false}; /* is the widget focused */ @@ -691,7 +697,11 @@ public: double m_undercurl_thickness{VTE_LINE_WIDTH}; /* Style stuff */ +#if VTE_GTK == 3 GtkBorder m_padding{1, 1, 1, 1}; +#elif VTE_GTK == 4 + GtkBorder m_padding{0, 0, 0, 0}; +#endif auto padding() const noexcept { return &m_padding; } vte::glib::RefPtr<GtkAdjustment> m_vadjustment{}; @@ -806,6 +816,9 @@ public: /* The allocation of the widget */ cairo_rectangle_int_t m_allocated_rect; + + constexpr auto const* allocated_rect() const noexcept { return &m_allocated_rect; } + /* The usable view area. This is the allocation, minus the padding, but * including additional right/bottom area if the allocation is not grid aligned. */ @@ -872,17 +885,32 @@ public: void widget_mouse_enter(vte::platform::MouseEvent const& event); void widget_mouse_leave(vte::platform::MouseEvent const& event); bool widget_mouse_scroll(vte::platform::ScrollEvent const& event); +#if VTE_GTK == 3 void widget_draw(cairo_t *cr); - void widget_get_preferred_width(int *minimum_width, - int *natural_width); - void widget_get_preferred_height(int *minimum_height, - int *natural_height); - void widget_size_allocate(GtkAllocation *allocation); +#endif /* VTE_GTK == 3 */ + void widget_measure_width(int *minimum_width, + int *natural_width); + void widget_measure_height(int *minimum_height, + int *natural_height); + +#if VTE_GTK == 3 + void widget_size_allocate(int x, + int y, + int width, + int height, + int baseline); +#elif VTE_GTK == 4 + void widget_size_allocate(int width, + int height, + int baseline); +#endif /* VTE_GTK */ void set_blink_settings(bool blink, int blink_time, int blink_timeout) noexcept; + void draw(cairo_t *cr, + cairo_region_t const* region); void paint_cursor(); void paint_im_preedit_string(); void draw_cells(vte::view::DrawingContext::TextRequest* items, @@ -1119,26 +1147,53 @@ public: bool rowcol_from_event(vte::platform::MouseEvent const& event, long *column, long *row); +#if VTE_GTK == 4 + bool rowcol_at(double x, + double y, + long* column, + long* row); +#endif char *hyperlink_check(vte::platform::MouseEvent const& event); + char *hyperlink_check_at(double x, + double y); + char *hyperlink_check(vte::grid::column_t column, + vte::grid::row_t row); bool regex_match_check_extra(vte::platform::MouseEvent const& event, vte::base::Regex const** regexes, size_t n_regexes, uint32_t match_flags, char** matches); + bool regex_match_check_extra_at(double x, + double y, + vte::base::Regex const** regexes, + size_t n_regexes, + uint32_t match_flags, + char** matches); + bool regex_match_check_extra(vte::grid::column_t column, + vte::grid::row_t row, + vte::base::Regex const** regexes, + size_t n_regexes, + uint32_t match_flags, + char** matches); char *regex_match_check(vte::grid::column_t column, vte::grid::row_t row, int *tag); char *regex_match_check(vte::platform::MouseEvent const& event, int *tag); + char *regex_match_check_at(double x, + double y, + int *tag); void regex_match_remove(int tag) noexcept; void regex_match_remove_all() noexcept; void regex_match_set_cursor(int tag, GdkCursor *gdk_cursor); + #if VTE_GTK == 3 void regex_match_set_cursor(int tag, GdkCursorType cursor_type); + #endif void regex_match_set_cursor(int tag, char const* cursor_name); bool match_rowcol_to_offset(vte::grid::column_t column, diff --git a/src/vteseq.cc b/src/vteseq.cc index 1b76ff22..2a8d777d 100644 --- a/src/vteseq.cc +++ b/src/vteseq.cc @@ -9140,12 +9140,17 @@ Terminal::XTERM_WM(vte::parser::Sequence const& seq) /* FIMXE: this should really report the monitor's workarea, * or even just a fixed value. */ +#if VTE_GTK == 3 auto gdkscreen = gtk_widget_get_screen(m_widget); int height = gdk_screen_get_height(gdkscreen); int width = gdk_screen_get_width(gdkscreen); _vte_debug_print(VTE_DEBUG_EMULATION, "Reporting screen size as %dx%d cells.\n", height / int(m_cell_height), width / int(m_cell_width)); +#elif VTE_GTK == 4 + auto height = int(m_row_count * m_cell_height); + auto width = int(m_column_count * m_cell_width); +#endif reply(seq, VTE_REPLY_XTERM_WM, {9, height / int(m_cell_height), width / int(m_cell_width)}); diff --git a/src/widget.cc b/src/widget.cc index 25e4b242..a88d737b 100644 --- a/src/widget.cc +++ b/src/widget.cc @@ -32,6 +32,16 @@ #include "vteptyinternal.hh" #include "debug.h" +#if VTE_GTK == 4 +#include "graphene-glue.hh" +#endif + +#if VTE_GTK == 3 +#define VTE_STYLE_CLASS_MONOSPACE GTK_STYLE_CLASS_MONOSPACE +#elif VTE_GTK == 4 +#define VTE_STYLE_CLASS_MONOSPACE "monospace" +#endif + using namespace std::literals; namespace vte { @@ -142,11 +152,19 @@ Widget::Widget(VteTerminal* t) m_hscroll_policy{GTK_SCROLL_NATURAL}, m_vscroll_policy{GTK_SCROLL_NATURAL} { +#if VTE_GTK == 3 gtk_widget_set_can_focus(gtk(), true); +#endif + +#if VTE_GTK == 4 + gtk_widget_set_focusable(gtk(), true); +#endif +#if VTE_GTK == 3 /* We do our own redrawing. */ // FIXMEchpe is this still necessary? gtk_widget_set_redraw_on_allocate(gtk(), false); +#endif /* Until Terminal init is completely fixed, use zero'd memory */ auto place = g_malloc0(sizeof(vte::terminal::Terminal)); @@ -156,7 +174,7 @@ Widget::Widget(VteTerminal* t) Widget::~Widget() noexcept try { - g_signal_handlers_disconnect_matched(gtk_widget_get_settings(m_widget), + g_signal_handlers_disconnect_matched(m_settings.get(), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, this); @@ -175,19 +193,38 @@ void Widget::beep() noexcept { if (realized()) - gdk_window_beep(gtk_widget_get_window(m_widget)); + gtk_widget_error_bell(gtk()); } +#if VTE_GTK == 4 + +bool +Widget::contains(double x, + double y) +{ + return false; +} + +#endif /* VTE_GTK == 4 */ + vte::glib::RefPtr<GdkCursor> Widget::create_cursor(std::string const& name) const noexcept { +#if VTE_GTK == 3 return vte::glib::take_ref(gdk_cursor_new_from_name(gtk_widget_get_display(m_widget), name.c_str())); +#elif VTE_GTK == 4 + return vte::glib::take_ref(gdk_cursor_new_from_name(name.c_str(), nullptr /* fallback */)); +#endif } void Widget::set_cursor(GdkCursor* cursor) noexcept { +#if VTE_GTK == 3 gdk_window_set_cursor(m_event_window, cursor); +#elif VTE_GTK == 4 + gtk_widget_set_cursor(gtk(), cursor); +#endif } void @@ -196,24 +233,36 @@ Widget::set_cursor(Cursor const& cursor) noexcept if (!realized()) return; - auto display = gtk_widget_get_display(m_widget); GdkCursor* gdk_cursor{nullptr}; switch (cursor.index()) { case 0: - gdk_cursor = gdk_cursor_new_from_name(display, std::get<0>(cursor).c_str()); +#if VTE_GTK == 3 + gdk_cursor = gdk_cursor_new_from_name(gtk_widget_get_display(gtk()), + std::get<0>(cursor).c_str()); +#elif VTE_GTK == 4 + gdk_cursor = gdk_cursor_new_from_name(std::get<0>(cursor).c_str(), + nullptr /* fallback */); +#endif /* VTE_GTK */ break; + case 1: gdk_cursor = std::get<1>(cursor).get(); - if (gdk_cursor != nullptr && - gdk_cursor_get_display(gdk_cursor) == display) { + if (gdk_cursor != nullptr +#if VTE_GTK == 3 + && gdk_cursor_get_display(gdk_cursor) == gtk_widget_get_display(gtk()) +#endif + ) { g_object_ref(gdk_cursor); } else { gdk_cursor = nullptr; } break; + +#if VTE_GTK == 3 case 2: - gdk_cursor = gdk_cursor_new_for_display(display, std::get<2>(cursor)); + gdk_cursor = gdk_cursor_new_for_display(gtk_widget_get_display(gtk()), std::get<2>(cursor)); break; +#endif } set_cursor(gdk_cursor); @@ -225,8 +274,8 @@ Clipboard& Widget::clipboard_get(ClipboardType type) const { switch (type) { - case ClipboardType::CLIPBOARD: return *m_clipboard; case ClipboardType::PRIMARY: return *m_primary_clipboard; + case ClipboardType::CLIPBOARD: return *m_clipboard; default: g_assert_not_reached(); throw std::runtime_error{""}; break; } } @@ -290,15 +339,60 @@ Widget::clipboard_set_text(ClipboardType type, clipboard_get(type).set_text(str); } +#if VTE_GTK == 4 + +std::pair<bool, bool> +Widget::compute_expand() +{ + return {true, true}; +} + +#endif /* VTE_GTK == 4 */ + void Widget::constructed() noexcept { +#if VTE_GTK == 3 + auto context = gtk_widget_get_style_context(m_widget); + gtk_style_context_add_class (context, VTE_STYLE_CLASS_MONOSPACE); +#elif VTE_GTK == 4 + gtk_widget_add_css_class(gtk(), VTE_STYLE_CLASS_MONOSPACE); +#endif /* VTE_GTK */ + +#if VTE_GTK == 4 + + connect_settings(); + +#endif /* VTE_GTK == 4 */ + +#if VTE_GTK == 3 /* Set the style as early as possible, before GTK+ starts * invoking various callbacks. This is needed in order to * compute the initial geometry correctly in presence of * non-default padding, see bug 787710. */ style_updated(); +#elif VTE_GTK == 4 + padding_changed(); +#endif /* VTE_GTK */ +} + +#if VTE_GTK == 4 + +void +Widget::css_changed(GtkCssStyleChange* change) +{ + /* This function is mostly useless, since there's no public API for GtkCssStyleChange */ + + padding_changed(); +} + +#endif /* VTE_GTK == 4 */ + +void +Widget::direction_changed(GtkTextDirection old_direction) noexcept +{ + // FIXME: does this need to feed to BiDi somehow? } void @@ -330,10 +424,123 @@ Widget::im_filter_keypress(KeyEvent const& event) noexcept // FIXMEchpe this can only be called when realized, so the m_im_context check is redundant return m_im_context && gtk_im_context_filter_keypress(m_im_context.get(), - reinterpret_cast<GdkEventKey*>(event.platform_event())); +#if VTE_GTK == 3 + reinterpret_cast<GdkEventKey*>(event.platform_event()) +#elif VTE_GTK == 4 + event.platform_event() +#endif + ); +} + +#if VTE_GTK == 3 + +void +Widget::event_focus_in(GdkEventFocus *event) +{ + _vte_debug_print(VTE_DEBUG_EVENTS, "Focus In"); + + m_terminal->widget_focus_in(); +} + +void +Widget::event_focus_out(GdkEventFocus *event) +{ + _vte_debug_print(VTE_DEBUG_EVENTS, "Focus Out"); + + m_terminal->widget_focus_out(); +} + +bool +Widget::event_key_press(GdkEventKey *event) +{ + auto key_event = key_event_from_gdk(reinterpret_cast<GdkEvent*>(event)); + + _vte_debug_print(VTE_DEBUG_EVENTS, "Key press key=%x keycode=%x modifiers=%x\n", + key_event.keyval(), key_event.keycode(), key_event.modifiers()); + + return m_terminal->widget_key_press(key_event); +} + +bool +Widget::event_key_release(GdkEventKey *event) +{ + auto key_event = key_event_from_gdk(reinterpret_cast<GdkEvent*>(event)); + + _vte_debug_print(VTE_DEBUG_EVENTS, "Key release key=%x keycode=%x modifiers=%x\n", + key_event.keyval(), key_event.keycode(), key_event.modifiers()); + + return m_terminal->widget_key_release(key_event); +} + +bool +Widget::event_button_press(GdkEventButton *event) +{ + auto mouse_event = mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event)); + + _vte_debug_print(VTE_DEBUG_EVENTS, "Click press button=%d press_count=%d x=%.3f y=%.3f\n", + mouse_event.button_value(), mouse_event.press_count(), + mouse_event.x(), mouse_event.y()); + + return m_terminal->widget_mouse_press(mouse_event); +} + +bool +Widget::event_button_release(GdkEventButton *event) +{ + auto mouse_event = mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event)); + + _vte_debug_print(VTE_DEBUG_EVENTS, "Click release button=%d x=%.3f y=%.3f\n", + mouse_event.button_value(), mouse_event.x(), mouse_event.y()); + + return m_terminal->widget_mouse_release(mouse_event); } void +Widget::event_enter(GdkEventCrossing *event) +{ + auto mouse_event = mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event)); + + _vte_debug_print(VTE_DEBUG_EVENTS, "Motion enter x=%.3f y=%.3f\n", + mouse_event.x(), mouse_event.y()); + + m_terminal->widget_mouse_enter(mouse_event); +} + +void +Widget::event_leave(GdkEventCrossing *event) +{ + auto mouse_event = mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event)); + + _vte_debug_print(VTE_DEBUG_EVENTS, "Motion leave x=%.3f y=%.3f\n", + mouse_event.x(), mouse_event.y()); + + m_terminal->widget_mouse_leave(mouse_event); +} +bool +Widget::event_scroll(GdkEventScroll *event) +{ + auto scroll_event = scroll_event_from_gdk(reinterpret_cast<GdkEvent*>(event)); + + _vte_debug_print(VTE_DEBUG_EVENTS, "Scroll delta_x=%.3f delta_y=%.3f\n", + scroll_event.dx(), scroll_event.dy()); + + return m_terminal->widget_mouse_scroll(scroll_event); +} + +bool +Widget::event_motion_notify(GdkEventMotion *event) +{ + auto mouse_event = mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event)); + + _vte_debug_print(VTE_DEBUG_EVENTS, "Motion x=%.3f y=%.3f\n", + mouse_event.x(), mouse_event.y()); + + return m_terminal->widget_mouse_motion(mouse_event); +} + +#endif /* VTE_GTK == 3 */ + +void Widget::im_focus_in() noexcept { gtk_im_context_focus_in(m_im_context.get()); @@ -368,6 +575,8 @@ Widget::im_set_cursor_location(cairo_rectangle_int_t const* rect) noexcept gtk_im_context_set_cursor_location(m_im_context.get(), rect); } +#if VTE_GTK == 3 + unsigned Widget::read_modifiers_from_gdk(GdkEvent* event) const noexcept { @@ -376,11 +585,9 @@ Widget::read_modifiers_from_gdk(GdkEvent* event) const noexcept if (!gdk_event_get_state(event, &mods)) return 0; - #if 1 /* HACK! Treat META as ALT; see bug #663779. */ if (mods & GDK_META_MASK) mods = GdkModifierType(mods | GDK_MOD1_MASK); - #endif /* Map non-virtual modifiers to virtual modifiers (Super, Hyper, Meta) */ auto display = gdk_window_get_display(gdk_event_get_window(event)); @@ -390,9 +597,12 @@ Widget::read_modifiers_from_gdk(GdkEvent* event) const noexcept return unsigned(mods); } +#endif /* VTE_GTK == 3 */ + unsigned Widget::key_event_translate_ctrlkey(KeyEvent const& event) const noexcept { +#if VTE_GTK == 3 if (event.keyval() < 128) return event.keyval(); @@ -417,33 +627,55 @@ Widget::key_event_translate_ctrlkey(KeyEvent const& event) const noexcept } return keyval; +#elif VTE_GTK == 4 + // FIXMEgtk4: find a way to do this on gtk4 + return event.keyval(); +#endif } KeyEvent -Widget::key_event_from_gdk(GdkEventKey* event) const +Widget::key_event_from_gdk(GdkEvent* event) const { auto type = EventBase::Type{}; - switch (gdk_event_get_event_type(reinterpret_cast<GdkEvent*>(event))) { + switch (gdk_event_get_event_type(event)) { case GDK_KEY_PRESS: type = KeyEvent::Type::eKEY_PRESS; break; case GDK_KEY_RELEASE: type = KeyEvent::Type::eKEY_RELEASE; break; default: g_assert_not_reached(); return {}; } - auto base_event = reinterpret_cast<GdkEvent*>(event); - return {base_event, +#if VTE_GTK == 3 + auto keyval = unsigned{}; + gdk_event_get_keyval(event, &keyval); + auto const scancode = unsigned(reinterpret_cast<GdkEventKey*>(event)->hardware_keycode); + auto const group = reinterpret_cast<GdkEventKey*>(event)->group; + auto const is_modifier = reinterpret_cast<GdkEventKey*>(event)->is_modifier != 0; +#elif VTE_GTK == 4 + auto keyval = gdk_key_event_get_keyval(event); + auto scancode = gdk_key_event_get_keycode(event); + auto const group = gdk_key_event_get_level(event); + auto const is_modifier = gdk_key_event_is_modifier(event) != false; +#endif /* VTE_GTK */ + + return {event, type, - read_modifiers_from_gdk(base_event), - event->keyval, - event->hardware_keycode, // gdk_event_get_scancode(event), - event->group, - event->is_modifier != 0}; +#if VTE_GTK == 3 + read_modifiers_from_gdk(event), +#elif VTE_GTK == 4 + gdk_event_get_modifier_state(event), +#endif + keyval, + scancode, + group, + is_modifier}; } +#if VTE_GTK == 3 + MouseEvent Widget::mouse_event_from_gdk(GdkEvent* event) const /* throws */ { auto type = EventBase::Type{}; - auto press_count = 0u; + auto press_count = 0; switch (gdk_event_get_event_type(event)) { case GDK_2BUTTON_PRESS: type = MouseEvent::Type::eMOUSE_PRESS; @@ -478,7 +710,7 @@ Widget::mouse_event_from_gdk(GdkEvent* event) const /* throws */ !gdk_event_get_coords(event, &x, &y)) x = y = -1.; // FIXMEchpe or throw? - auto button = unsigned{0}; + auto button = 0u; (void)gdk_event_get_button(event, &button); return {type, @@ -512,24 +744,79 @@ Widget::scroll_event_from_gdk(GdkEvent* event) const /* throws */ dx, dy}; } +#endif /* VTE_GTK == 3 */ + void Widget::map() noexcept { +#if VTE_GTK == 3 if (m_event_window) gdk_window_show_unraised(m_event_window); +#endif +} + +#if VTE_GTK == 4 + +void +Widget::measure(GtkOrientation orientation, + int for_size, + int* minimum, + int* natural, + int* minimum_baseline, + int* natural_baseline) +{ + _vte_debug_print(VTE_DEBUG_WIDGET_SIZE, "Widget measure for_size=%d orientation=%s\n", + for_size, + orientation == GTK_ORIENTATION_HORIZONTAL ? "horizontal" : "vertical"); + + switch (orientation) { + case GTK_ORIENTATION_HORIZONTAL: + terminal()->widget_measure_width(minimum, natural); + break; + case GTK_ORIENTATION_VERTICAL: + *minimum_baseline = *natural_baseline = 0; + terminal()->widget_measure_height(minimum, natural); + break; + } +} + +#endif /* VTE_GTK == 4 */ + +void +Widget::padding_changed() +{ +#if VTE_GTK == 3 + auto padding = GtkBorder{}; + auto context = gtk_widget_get_style_context(gtk()); + gtk_style_context_get_padding(context, +#if VTE_GTK == 3 + gtk_style_context_get_state(context), +#endif + &padding); + terminal()->set_border_padding(&padding); +#endif /* VTE_GTK FIXMEgtk4 how to handle margin/padding? */ } bool Widget::primary_paste_enabled() const noexcept { auto primary_paste = gboolean{}; - g_object_get(gtk_widget_get_settings(gtk()), + g_object_get(m_settings.get(), "gtk-enable-primary-paste", &primary_paste, nullptr); return primary_paste != false; } +bool +Widget::query_tooltip(int x, + int y, + bool keyboard, + GtkTooltip* tooltip) noexcept +{ + return false; +} + void Widget::realize() noexcept { @@ -545,6 +832,7 @@ Widget::realize() noexcept else m_hyperlink_cursor = create_cursor(VTE_HYPERLINK_CURSOR); +#if VTE_GTK == 3 /* Create an input window for the widget. */ auto allocation = m_terminal->get_allocated_rect(); GdkWindowAttr attributes; @@ -579,15 +867,21 @@ Widget::realize() noexcept m_event_window = gdk_window_new(gtk_widget_get_parent_window (m_widget), &attributes, attributes_mask); gtk_widget_register_window(m_widget, m_event_window); +#endif /* VTE_GTK == 3 */ assert(!m_im_context); - m_im_context.reset(gtk_im_multicontext_new()); -#if GTK_CHECK_VERSION (3, 24, 14) + m_im_context = vte::glib::take_ref(gtk_im_multicontext_new()); +#if (VTE_GTK == 3 && GTK_CHECK_VERSION (3, 24, 14)) || VTE_GTK == 4 g_object_set(m_im_context.get(), "input-purpose", GTK_INPUT_PURPOSE_TERMINAL, nullptr); #endif + +#if VTE_GTK == 3 gtk_im_context_set_client_window(m_im_context.get(), m_event_window); +#elif VTE_GTK == 4 + gtk_im_context_set_client_widget(m_im_context.get(), gtk()); +#endif g_signal_connect(m_im_context.get(), "commit", G_CALLBACK(im_commit_cb), this); g_signal_connect(m_im_context.get(), "preedit-start", @@ -608,42 +902,82 @@ Widget::realize() noexcept m_terminal->widget_realize(); } +#if VTE_GTK == 4 + +void +Widget::root() +{ +} + +#endif /* VTE_GTK == 4 */ + +#if VTE_GTK == 3 + void Widget::screen_changed(GdkScreen *previous_screen) noexcept { auto gdk_screen = gtk_widget_get_screen(m_widget); - if (previous_screen != nullptr && - (gdk_screen != previous_screen || gdk_screen == nullptr)) { - auto settings = gtk_settings_get_for_screen(previous_screen); - g_signal_handlers_disconnect_matched(settings, G_SIGNAL_MATCH_DATA, + if (gdk_screen == previous_screen || gdk_screen == nullptr) + return; + + connect_settings(); +} + +#elif VTE_GTK == 4 + +void +Widget::display_changed() noexcept +{ + /* There appears to be no way to retrieve the previous display */ + connect_settings(); +} + +#endif /* VTE_GTK */ + +void +Widget::connect_settings() +{ + auto settings = vte::glib::make_ref(gtk_widget_get_settings(m_widget)); + if (settings == m_settings) + return; + + if (m_settings) + g_signal_handlers_disconnect_matched(m_settings.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); - } - if (gdk_screen == previous_screen || gdk_screen == nullptr) - return; + m_settings = std::move(settings); settings_changed(); - auto settings = gtk_widget_get_settings(m_widget); - g_signal_connect (settings, "notify::gtk-cursor-blink", - G_CALLBACK(settings_notify_cb), this); - g_signal_connect (settings, "notify::gtk-cursor-blink-time", - G_CALLBACK(settings_notify_cb), this); - g_signal_connect (settings, "notify::gtk-cursor-blink-timeout", - G_CALLBACK(settings_notify_cb), this); + g_signal_connect(m_settings.get(), "notify::gtk-cursor-blink", + G_CALLBACK(settings_notify_cb), this); + g_signal_connect(m_settings.get(), "notify::gtk-cursor-blink-time", + G_CALLBACK(settings_notify_cb), this); + g_signal_connect(m_settings.get(), "notify::gtk-cursor-blink-timeout", + G_CALLBACK(settings_notify_cb), this); +#if VTE_GTK == 4 + g_signal_connect(m_settings.get(), "notify::gtk-cursor-aspect-ratio", + G_CALLBACK(settings_notify_cb), this); +#endif } void -Widget::settings_changed() noexcept +Widget::settings_changed() { auto blink = gboolean{}; auto blink_time = int{}; auto blink_timeout = int{}; - g_object_get(gtk_widget_get_settings(m_widget), +#if VTE_GTK == 4 + auto aspect = double{}; +#endif + g_object_get(m_settings.get(), "gtk-cursor-blink", &blink, "gtk-cursor-blink-time", &blink_time, "gtk-cursor-blink-timeout", &blink_timeout, +#if VTE_GTK == 4 + "gtk-cursor-aspect-ratio", &aspect, +#endif nullptr); _vte_debug_print(VTE_DEBUG_MISC, @@ -651,6 +985,10 @@ Widget::settings_changed() noexcept blink, blink_time, blink_timeout); m_terminal->set_blink_settings(blink, blink_time, blink_timeout); + +#if VTE_GTK == 4 + m_terminal->set_cursor_aspect(aspect); +#endif } void @@ -664,6 +1002,30 @@ Widget::set_cursor(CursorType type) noexcept } } +void +Widget::set_hscroll_policy(GtkScrollablePolicy policy) +{ + m_hscroll_policy = policy; + +#if VTE_GTK == 3 + gtk_widget_queue_resize_no_redraw(gtk()); +#elif VTE_GTK == 4 + gtk_widget_queue_resize(gtk()); +#endif +} + +void +Widget::set_vscroll_policy(GtkScrollablePolicy policy) +{ + m_vscroll_policy = policy; + +#if VTE_GTK == 3 + gtk_widget_queue_resize_no_redraw(gtk()); +#elif VTE_GTK == 4 + gtk_widget_queue_resize(gtk()); +#endif +} + bool Widget::set_pty(VtePty* pty_obj) noexcept { @@ -704,10 +1066,19 @@ Widget::unset_pty() noexcept g_object_notify_by_pspec(object(), pspecs[PROP_PTY]); } +#if VTE_GTK == 3 + void -Widget::size_allocate(GtkAllocation* allocation) noexcept +Widget::size_allocate(GtkAllocation* allocation) { - m_terminal->widget_size_allocate(allocation); + _vte_debug_print(VTE_DEBUG_WIDGET_SIZE, "Widget size allocate width=%d height=%d x=%d y=%d\n", + allocation->width, allocation->height, allocation->x, allocation->y); + + m_terminal->widget_size_allocate(allocation->x, allocation->y, + allocation->width, allocation->height, + -1); + + gtk_widget_set_allocation(gtk(), allocation); if (realized()) gdk_window_move_resize(m_event_window, @@ -717,6 +1088,23 @@ Widget::size_allocate(GtkAllocation* allocation) noexcept allocation->height); } +#elif VTE_GTK == 4 + +void +Widget::size_allocate(int width, + int height, + int baseline) +{ + _vte_debug_print(VTE_DEBUG_WIDGET_SIZE, "Widget size allocate width=%d height=%d baseline=%d\n", + width, height, baseline); + + terminal()->widget_size_allocate(width, height, baseline); + + gtk_widget_allocate(gtk(), width, height, baseline, nullptr); +} + +#endif /* VTE_GTK */ + bool Widget::should_emit_signal(int id) noexcept { @@ -727,13 +1115,35 @@ Widget::should_emit_signal(int id) noexcept } void +Widget::state_flags_changed(GtkStateFlags old_flags) +{ + _vte_debug_print(VTE_DEBUG_STYLE, "Widget state flags changed\n"); +} + +#if VTE_GTK == 4 + +void +Widget::snapshot(GtkSnapshot* snapshot_object) +{ + _vte_debug_print(VTE_DEBUG_DRAW, "Widget snapshot\n"); + + auto rect = terminal()->allocated_rect(); + auto region = vte::take_freeable(cairo_region_create_rectangle(rect)); + auto grect = vte::graphene::make_rect(rect); + auto cr = vte::take_freeable(gtk_snapshot_append_cairo(snapshot_object, &grect)); + terminal()->draw(cr.get(), region.get()); +} + +#endif /* VTE_GTK == 4 */ + +#if VTE_GTK == 3 + +void Widget::style_updated() noexcept { - auto padding = GtkBorder{}; - auto context = gtk_widget_get_style_context(gtk()); - gtk_style_context_get_padding(context, gtk_style_context_get_state(context), - &padding); - m_terminal->set_border_padding(&padding); + _vte_debug_print(VTE_DEBUG_STYLE, "Widget style changed\n"); + + padding_changed(); auto aspect = float{}; gtk_widget_style_get(gtk(), "cursor-aspect-ratio", &aspect, nullptr); @@ -742,13 +1152,48 @@ Widget::style_updated() noexcept m_terminal->widget_style_updated(); } +#endif /* VTE_GTK == 3 */ + +#if VTE_GTK == 4 + +void +Widget::system_setting_changed(GtkSystemSetting setting) +{ + _vte_debug_print(VTE_DEBUG_STYLE, "Widget system settings %d changed\n", int(setting)); + + switch (setting) { + case GTK_SYSTEM_SETTING_DISPLAY: + display_changed(); + break; + + case GTK_SYSTEM_SETTING_DPI: + break; + + case GTK_SYSTEM_SETTING_FONT_CONFIG: + break; + + case GTK_SYSTEM_SETTING_FONT_NAME: + break; + + case GTK_SYSTEM_SETTING_ICON_THEME: + break; + + default: + break; + } +} + +#endif /* VTE_GTK == 4 */ + void Widget::unmap() noexcept { m_terminal->widget_unmap(); +#if VTE_GTK == 3 if (m_event_window) gdk_window_hide(m_event_window); +#endif } void @@ -756,16 +1201,17 @@ Widget::unrealize() noexcept { m_terminal->widget_unrealize(); + // FIXMEgtk4 only withdraw content from clipboard, not unselect? if (m_clipboard) { terminal()->widget_clipboard_data_clear(*m_clipboard); m_clipboard->disown(); + m_clipboard.reset(); } if (m_primary_clipboard) { terminal()->widget_clipboard_data_clear(*m_primary_clipboard); m_primary_clipboard->disown(); + m_primary_clipboard.reset(); } - m_clipboard.reset(); - m_primary_clipboard.reset(); m_default_cursor.reset(); m_invisible_cursor.reset(); @@ -779,15 +1225,30 @@ Widget::unrealize() noexcept 0, 0, NULL, NULL, this); m_terminal->im_preedit_reset(); +#if VTE_GTK == 3 gtk_im_context_set_client_window(m_im_context.get(), nullptr); +#elif VTE_GTK == 4 + gtk_im_context_set_client_widget(m_im_context.get(), nullptr); +#endif m_im_context.reset(); +#if VTE_GTK == 3 /* Destroy input window */ gtk_widget_unregister_window(m_widget, m_event_window); gdk_window_destroy(m_event_window); m_event_window = nullptr; +#endif /* VTE_GTK == 3 */ } +#if VTE_GTK == 4 + +void +Widget::unroot() +{ +} + +#endif /* VTE_GTK == 4 */ + } // namespace platform } // namespace vte diff --git a/src/widget.hh b/src/widget.hh index ede09be3..813b3625 100644 --- a/src/widget.hh +++ b/src/widget.hh @@ -20,6 +20,8 @@ #include <memory> #include <optional> #include <string> +#include <tuple> +#include <utility> #include <variant> #include "vteterminal.h" @@ -95,7 +97,7 @@ protected: unsigned modifiers, unsigned keyval, unsigned keycode, - uint8_t group, + unsigned group, bool is_modifier) noexcept : EventBase{type}, m_platform_event{gdk_event}, @@ -126,12 +128,23 @@ public: constexpr auto is_key_press() const noexcept { return type() == Type::eKEY_PRESS; } constexpr auto is_key_release() const noexcept { return type() == Type::eKEY_RELEASE; } + bool matches(unsigned keyval, + unsigned modifiers) const noexcept + { +#if VTE_GTK == 3 + return false; // FIXMEgtk3 +#elif VTE_GTK == 4 + return gdk_key_event_matches(platform_event(), + keyval, GdkModifierType(modifiers)) == GDK_KEY_MATCH_EXACT; +#endif + } + private: GdkEvent* m_platform_event; unsigned m_modifiers; unsigned m_keyval; unsigned m_keycode; - uint8_t m_group; + unsigned m_group; bool m_is_modifier; }; // class KeyEvent @@ -154,7 +167,7 @@ protected: MouseEvent() noexcept = default; constexpr MouseEvent(Type type, - unsigned press_count, + int press_count, unsigned modifiers, Button button, double x, @@ -190,7 +203,7 @@ public: constexpr auto is_mouse_release() const noexcept { return type() == Type::eMOUSE_RELEASE; } private: - unsigned m_press_count; + int m_press_count; unsigned m_modifiers; Button m_button; double m_x; @@ -257,24 +270,60 @@ public: void unrealize() noexcept; void map() noexcept; void unmap() noexcept; + void state_flags_changed(GtkStateFlags old_flags); + void direction_changed(GtkTextDirection old_direction) noexcept; + bool query_tooltip(int x, + int y, + bool keyboard, + GtkTooltip* tooltip) noexcept; + + void connect_settings(); + void padding_changed(); + void settings_changed(); + +#if VTE_GTK == 3 void style_updated() noexcept; void draw(cairo_t *cr) noexcept { m_terminal->widget_draw(cr); } void get_preferred_width(int *minimum_width, - int *natural_width) const noexcept { m_terminal->widget_get_preferred_width(minimum_width, natural_width); } + int *natural_width) const noexcept { m_terminal->widget_measure_width(minimum_width, natural_width); } void get_preferred_height(int *minimum_height, - int *natural_height) const noexcept { m_terminal->widget_get_preferred_height(minimum_height, natural_height); } - void size_allocate(GtkAllocation *allocation) noexcept; - - void focus_in(GdkEventFocus *event) noexcept { m_terminal->widget_focus_in(); } - void focus_out(GdkEventFocus *event) noexcept { m_terminal->widget_focus_out(); } - bool key_press(GdkEventKey *event) noexcept { return m_terminal->widget_key_press(key_event_from_gdk(event)); } - bool key_release(GdkEventKey *event) noexcept { return m_terminal->widget_key_release(key_event_from_gdk(event)); } - bool button_press(GdkEventButton *event) noexcept { return m_terminal->widget_mouse_press(mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event))); } - bool button_release(GdkEventButton *event) noexcept { return m_terminal->widget_mouse_release(mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event))); } - void enter(GdkEventCrossing *event) noexcept { m_terminal->widget_mouse_enter(mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event))); } - void leave(GdkEventCrossing *event) noexcept { m_terminal->widget_mouse_leave(mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event))); } - bool scroll(GdkEventScroll *event) noexcept { return m_terminal->widget_mouse_scroll(scroll_event_from_gdk(reinterpret_cast<GdkEvent*>(event))); } - bool motion_notify(GdkEventMotion *event) noexcept { return m_terminal->widget_mouse_motion(mouse_event_from_gdk(reinterpret_cast<GdkEvent*>(event))); } + int *natural_height) const noexcept { m_terminal->widget_measure_height(minimum_height, natural_height); } + void size_allocate(GtkAllocation *allocation); + + void event_focus_in(GdkEventFocus *event); + void event_focus_out(GdkEventFocus *event); + bool event_key_press(GdkEventKey *event); + bool event_key_release(GdkEventKey *event); + bool event_button_press(GdkEventButton *event); + bool event_button_release(GdkEventButton *event); + void event_enter(GdkEventCrossing *event); + void event_leave(GdkEventCrossing *event); + bool event_scroll(GdkEventScroll *event); + bool event_motion_notify(GdkEventMotion *event); + + void screen_changed (GdkScreen *previous_screen) noexcept; +#endif /* VTE_GTK == 3 */ + +#if VTE_GTK == 4 + void size_allocate(int width, + int height, + int baseline); + void root(); + void unroot(); + void measure(GtkOrientation orientation, + int for_size, + int* minimum, + int* natural, + int* minimum_baseline, + int* natural_baseline); + std::pair<bool, bool> compute_expand(); + void css_changed(GtkCssStyleChange* change); + void system_setting_changed(GtkSystemSetting setting); + void snapshot(GtkSnapshot* snapshot_object); + bool contains(double x, + double y); + void display_changed() noexcept; +#endif /* VTE_GTK == 4 */ void grab_focus() noexcept { gtk_widget_grab_focus(gtk()); } @@ -291,17 +340,14 @@ public: void copy(vte::platform::ClipboardType type, vte::platform::ClipboardFormat format) noexcept { m_terminal->widget_copy(type, format); } - void screen_changed (GdkScreen *previous_screen) noexcept; - void settings_changed() noexcept; - void beep() noexcept; - void set_hadjustment(vte::glib::RefPtr<GtkAdjustment>&& adjustment) noexcept { m_hadjustment = std::move(adjustment); } - void set_vadjustment(vte::glib::RefPtr<GtkAdjustment>&& adjustment) { terminal()->widget_set_vadjustment(std::move(adjustment)); } + void set_hadjustment(vte::glib::RefPtr<GtkAdjustment> adjustment) noexcept { m_hadjustment = std::move(adjustment); } + void set_vadjustment(vte::glib::RefPtr<GtkAdjustment> adjustment) { terminal()->widget_set_vadjustment(std::move(adjustment)); } auto hadjustment() noexcept { return m_hadjustment.get(); } auto vadjustment() noexcept { return terminal()->vadjustment(); } - void set_hscroll_policy(GtkScrollablePolicy policy) noexcept { m_hscroll_policy = policy; } - void set_vscroll_policy(GtkScrollablePolicy policy) noexcept { m_vscroll_policy = policy; } + void set_hscroll_policy(GtkScrollablePolicy policy); + void set_vscroll_policy(GtkScrollablePolicy policy); auto hscroll_policy() const noexcept { return m_hscroll_policy; } auto vscroll_policy() const noexcept { return m_vscroll_policy; } auto padding() const noexcept { return terminal()->padding(); } @@ -346,12 +392,15 @@ public: return terminal()->regex_match_check(column, row, tag); } +#if VTE_GTK == 3 + char* regex_match_check(GdkEvent* event, int* tag) { return terminal()->regex_match_check(mouse_event_from_gdk(event), tag); } + bool regex_match_check_extra(GdkEvent* event, vte::base::Regex const** regexes, size_t n_regexes, @@ -367,6 +416,8 @@ public: return terminal()->hyperlink_check(mouse_event_from_gdk(event)); } +#endif /* VTE_GTK */ + bool should_emit_signal(int id) noexcept; bool set_sixel_enabled(bool enabled) noexcept { return m_terminal->set_sixel_enabled(enabled); } @@ -381,7 +432,9 @@ protected: eHyperlink }; +#if VTE_GTK == 3 GdkWindow* event_window() const noexcept { return m_event_window; } +#endif bool realized() const noexcept { @@ -415,10 +468,12 @@ public: // FIXMEchpe void im_preedit_changed() noexcept; private: + KeyEvent key_event_from_gdk(GdkEvent* event) const; +#if VTE_GTK == 3 unsigned read_modifiers_from_gdk(GdkEvent* event) const noexcept; - KeyEvent key_event_from_gdk(GdkEventKey* event) const; MouseEvent mouse_event_from_gdk(GdkEvent* event) const /* throws */; ScrollEvent scroll_event_from_gdk(GdkEvent* event) const /* throws */; +#endif void clipboard_request_received_cb(Clipboard const& clipboard, std::string_view const& text); @@ -432,8 +487,12 @@ private: vte::terminal::Terminal* m_terminal; +#if VTE_GTK == 3 /* Event window */ GdkWindow *m_event_window; +#endif + + vte::glib::RefPtr<GtkSettings> m_settings{nullptr}; /* Cursors */ vte::glib::RefPtr<GdkCursor> m_default_cursor; |