[vte] app: Add new test application
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vte] app: Add new test application
- Date: Sun, 24 Sep 2017 18:54:24 +0000 (UTC)
commit 039b22ad57c8d43adf7f00cef0095e3d4c2f6657
Author: Christian Persch <chpe src gnome org>
Date: Sun Sep 24 20:53:57 2017 +0200
app: Add new test application
Re-write the vala test application in C for maintainability, and
degrade the vala test app to a mere noinst programme there only
to check that the vala bindings work.
Remove the old C test application.
bindings/vala/Makefile.am | 24 +-
configure.ac | 16 +-
po/POTFILES.skip | 4 +
src/Makefile.am | 28 +-
src/app/Makefile.am | 57 ++
src/app/app.cc | 1915 +++++++++++++++++++++++++++++++++++++++++++++
src/app/app.gresource.xml | 24 +
src/app/appmenu.ui | 33 +
src/app/search-popover.ui | 265 +++++++
src/app/window.ui | 209 +++++
src/vteapp.c | 1260 -----------------------------
11 files changed, 2522 insertions(+), 1313 deletions(-)
---
diff --git a/bindings/vala/Makefile.am b/bindings/vala/Makefile.am
index 0078ad4..667af82 100644
--- a/bindings/vala/Makefile.am
+++ b/bindings/vala/Makefile.am
@@ -1,8 +1,4 @@
-if ENABLE_TEST_APPLICATION
-bin_PROGRAMS = vte-@VTE_API_MAJOR_VERSION@.@VTE_API_MINOR_VERSION@
-else
-noinst_PROGRAMS = vte-@VTE_API_MAJOR_VERSION@.@VTE_API_MINOR_VERSION@
-endif
+noinst_PROGRAMS = app
CLEANFILES =
DISTCLEANFILES =
@@ -30,17 +26,17 @@ CLEANFILES += $(nodist_vapi_DATA)
# VALA Test application
-vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_SOURCES = \
+app_SOURCES = \
app.vala \
config.vapi \
$(builddir)/vte-$(VTE_API_VERSION).vapi \
$(NULL)
-nodist_vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_SOURCES = \
+nodist_app_SOURCES = \
appresources.c \
$(NULL)
-vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_VALAFLAGS = \
+app_VALAFLAGS = \
--target-glib=2.38 \
--pkg=posix \
--pkg=gio-2.0 \
@@ -48,35 +44,35 @@ vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_VALAFLAGS = \
--gresources app.gresource.xml \
$(AM_VALAFLAGS)
-vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_CPPFLAGS = \
+app_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/vte \
-I$(top_builddir)/src/vte \
$(AM_CPPFLAGS) \
$(NULL)
-vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_CFLAGS = \
+app_CFLAGS = \
-Wno-unused-variable \
-Wno-unused-but-set-variable \
$(VTE_CFLAGS) \
$(AM_CFLAGS) \
$(NULL)
-vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_LDFLAGS = \
+app_LDFLAGS = \
$(AM_LDFLAGS) \
$(NULL)
-vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_LDADD = \
+app_LDADD = \
$(top_builddir)/src/libvte-$(VTE_API_VERSION).la \
$(VTE_LIBS) \
$(NULL)
if HAVE_VALA_0_31
-vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_VALAFLAGS += --disable-since-check
+app_VALAFLAGS += --disable-since-check
endif
if HAVE_GTK_3_16
-vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_VALAFLAGS += -D GTK_3_16
+app_VALAFLAGS += -D GTK_3_16
endif
CLEANFILES += \
diff --git a/configure.ac b/configure.ac
index fd42c5e..e5109ff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -10,7 +10,7 @@ AC_INIT([vte],
[vte])
AC_CONFIG_MACRO_DIR([m4])
-AC_CONFIG_SRCDIR([src/vteapp.c])
+AC_CONFIG_SRCDIR([src/app/app.cc])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([1.9 foreign no-dist-gzip dist-bzip2 -Wno-portability])
@@ -62,7 +62,6 @@ case "$with_gtk" in
enable_glade_catalogue=no
enable_introspection=no
enable_vala=no
- enable_test_application=no
;;
esac
@@ -384,16 +383,6 @@ m4_ifdef([VAPIGEN_CHECK],
fi
AM_CONDITIONAL([ENABLE_VAPIGEN],[false])])
-# ****************
-# Test Application
-# ****************
-
-AC_MSG_CHECKING([whether to install the vala test application])
-AC_ARG_ENABLE([test-application],
- [AS_HELP_STRING([--disable-test-application],[Disable installing the vala test application])],
- [],[enable_test_application=$enable_vala])
-AC_MSG_RESULT([$enable_test_application])
-
AM_PROG_VALAC([0.24.0],[],[
if test "$enable_vala" != "no"; then
AC_MSG_ERROR([Vala requested but vala compiler >= 0.24.0 not found])
@@ -402,7 +391,6 @@ AM_PROG_VALAC([0.24.0],[],[
PKG_CHECK_MODULES([GTK_3_16],[gtk+-$GTK_API_VERSION >= 3.16],[have_gtk_3_16=yes],[have_gtk_3_16=no])
AM_PROG_VALAC([0.31.1],[have_vala_0_31=yes],[have_vala_0_31=no])
-AM_CONDITIONAL([ENABLE_TEST_APPLICATION],[test "$enable_test_application" = "yes"])
AM_CONDITIONAL([HAVE_GTK_3_16],[test "$have_gtk_3_16" = "yes"])
AM_CONDITIONAL([HAVE_VALA_0_31],[test "$have_vala_0_31" = "yes"])
@@ -459,6 +447,7 @@ Makefile
src/Makefile
src/vte.gresource.xml
src/vte/vteversion.h
+src/app/Makefile
bindings/Makefile
bindings/gir/Makefile
bindings/vala/Makefile
@@ -491,5 +480,4 @@ Configuration for libvte $VERSION for gtk+-$GTK_API_VERSION
Debugging: $enable_debug
Introspection: $enable_introspection
Vala bindings: $enable_vala
- Test application: $enable_test_application
EOF
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 3c6c2fd..178669b 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -2,3 +2,7 @@ bindings/vala/app.ui
bindings/vala/search-popover.ui
src/vteapp.c
src/vtespawn.cc
+src/app/app.cc
+src/app/appmenu.ui
+src/app/search-popover.ui
+src/app/window.ui
diff --git a/src/Makefile.am b/src/Makefile.am
index 21acac6..75079a9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,7 @@
+SUBDIRS = . app
+
bin_PROGRAMS =
-noinst_PROGRAMS = testvte
+noinst_PROGRAMS =
CLEANFILES =
DISTCLEANFILES =
@@ -182,30 +184,6 @@ vteseq-list.h: vteseq-n.gperf
vteresources.cc: vte.gresource.xml Makefile $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies
vte.gresource.xml)
$(AM_V_GEN)$(GLIB_COMPILE_RESOURCES) --target $@ --sourcedir . --generate-source --c-name _vte $<
-# C Test application
-
-testvte_SOURCES = \
- vteapp.c \
- vtepcre2.h \
- debug.c \
- debug.h \
- $(NULL)
-
-testvte_CPPFLAGS = \
- -DGLIB_DISABLE_DEPRECATION_WARNINGS \
- -DGDK_DISABLE_DEPRECATION_WARNINGS \
- -I$(builddir)/vte \
- -I$(srcdir)/vte \
- $(AM_CPPFLAGS)
-
-testvte_CFLAGS = \
- $(VTE_CFLAGS) \
- $(AM_CFLAGS)
-
-testvte_LDADD = \
- libvte-$(VTE_API_VERSION).la \
- $(VTE_LIBS)
-
# Misc unit tests and utilities
noinst_PROGRAMS += interpret slowcat
diff --git a/src/app/Makefile.am b/src/app/Makefile.am
new file mode 100644
index 0000000..56f2331
--- /dev/null
+++ b/src/app/Makefile.am
@@ -0,0 +1,57 @@
+bin_PROGRAMS = vte-@VTE_API_MAJOR_VERSION@.@VTE_API_MINOR_VERSION@
+
+CLEANFILES =
+DISTCLEANFILES =
+MAINTAINERCLEANFILES =
+EXTRA_DIST =
+
+vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_SOURCES = \
+ app.cc \
+ $(NULL)
+
+nodist_vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_SOURCES = \
+ resources.cc \
+ $(NULL)
+
+BUILT_SOURCES = $(nodist_vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_SOURCES)
+
+vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Vte\" \
+ -DLOCALEDIR='"$(localedir)"' \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/vte \
+ -I$(top_builddir)/src/vte \
+ $(AM_CPPFLAGS) \
+ $(NULL)
+
+vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_CXXFLAGS = \
+ $(VTE_CFLAGS) \
+ $(WARN_CXXFLAGS) \
+ $(AM_CXXFLAGS) \
+ $(NULL)
+
+vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_LDFLAGS = \
+ $(AM_LDFLAGS) \
+ $(NULL)
+
+vte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_LDADD = \
+ $(top_builddir)/src/libvte-$(VTE_API_VERSION).la \
+ $(VTE_LIBS) \
+ $(NULL)
+
+CLEANFILES += \
+ resources.cc \
+ $(NULL)
+
+EXTRA_DIST += \
+ app.gresource.xml \
+ search-popover.ui \
+ window.ui \
+ $(NULL)
+
+resources.cc: app.gresource.xml Makefile $(shell $(GLIB_COMPILE_RESOURCES) --generate-dependencies
--sourcedir $(srcdir) $(srcdir)/app.gresource.xml)
+ $(AM_V_GEN) XMLLINT=$(XMLLINT) $(GLIB_COMPILE_RESOURCES) --target $@ --sourcedir $(srcdir) --generate
--c-name app $<
+
+.DELETE_ON_ERROR:
+
+-include $(top_srcdir)/git.mk
diff --git a/src/app/app.cc b/src/app/app.cc
new file mode 100644
index 0000000..142b95c
--- /dev/null
+++ b/src/app/app.cc
@@ -0,0 +1,1915 @@
+/*
+ * Copyright © 2001,2002 Red Hat, Inc.
+ * Copyright © 2014, 2017 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <locale.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <vte/vte.h>
+#include "vtepcre2.h"
+
+/* options */
+
+class Options {
+public:
+ bool allow_window_ops{false};
+ bool audible_bell{false};
+ bool console{false};
+ bool cursor_bg_color_set{false};
+ bool cursor_fg_color_set{false};
+ bool debug{false};
+ bool hl_bg_color_set{false};
+ bool hl_fg_color_set{false};
+ bool icon_title{false};
+ bool keep{false};
+ bool no_argb_visual{false};
+ bool no_builtin_dingus{false};
+ bool no_context_menu{false};
+ bool no_double_buffer{false};
+ bool no_geometry_hints{false};
+ bool no_hyperlink{false};
+ bool no_rewrap{false};
+ bool no_shell{false};
+ bool object_notifications{false};
+ bool reverse{false};
+ bool use_gregex{false};
+ bool version{false};
+ char* command{nullptr};
+ char* encoding{nullptr};
+ char* font_string{nullptr};
+ char* geometry{nullptr};
+ char* output_filename{nullptr};
+ char* word_char_exceptions{nullptr};
+ char* working_directory{nullptr};
+ char** dingus{nullptr};
+ char** exec_argv{nullptr};
+ char** environment{nullptr};
+ GdkRGBA cursor_bg_color{};
+ GdkRGBA cursor_fg_color{};
+ GdkRGBA hl_bg_color{};
+ GdkRGBA hl_fg_color{};
+ int cjk_ambiguous_width{1};
+ int extra_margin{-1};
+ int scrollback_lines{-1 /* infinite */};
+ int transparency_percent{-1};
+ int verbosity{0};
+ VteCursorBlinkMode cursor_blink_mode{VTE_CURSOR_BLINK_SYSTEM};
+ VteCursorShape cursor_shape{VTE_CURSOR_SHAPE_BLOCK};
+
+ ~Options() {
+ g_free(command);
+ g_free(encoding);
+ g_free(font_string);
+ g_free(geometry);
+ g_free(output_filename);
+ g_free(word_char_exceptions);
+ g_free(working_directory);
+ g_strfreev(dingus);
+ g_strfreev(exec_argv);
+ g_strfreev(environment);
+ }
+
+private:
+
+ bool parse_enum(GType type,
+ char const* str,
+ int* value,
+ GError** error)
+ {
+ GEnumClass* enum_klass = reinterpret_cast<GEnumClass*>(g_type_class_ref(type));
+ GEnumValue* enum_value = g_enum_get_value_by_nick(enum_klass, str);
+
+ if (enum_value == nullptr) {
+ g_type_class_unref(enum_klass);
+ g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+ "Failed to parse enum value \"%s\" as type \"%s\"",
+ str, g_quark_to_string(g_type_qname(type)));
+ return false;
+ }
+
+ *value = enum_value->value;
+ g_type_class_unref(enum_klass);
+ return true;
+ }
+
+ bool parse_width_enum(char const* str,
+ int& value,
+ GError** error)
+ {
+ int v = 1;
+ if (str == nullptr || g_str_equal(str, "narrow"))
+ v = 1;
+ else if (g_str_equal(str, "wide"))
+ v = 2;
+ else {
+ g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+ "Failed to parse \"%s\" as width (allowed values are \"narrow\" or
\"wide\")", str);
+ return false;
+ }
+
+ value = v;
+ return true;
+ }
+
+ bool parse_color(char const* str,
+ GdkRGBA* value,
+ bool* value_set,
+ GError** error)
+ {
+ GdkRGBA color;
+ if (!gdk_rgba_parse(&color, str)) {
+ g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+ "Failed to parse \"%s\" as color", str);
+ *value_set = false;
+ return false;
+ }
+
+ *value = color;
+ *value_set = true;
+ return true;
+ }
+
+ bool parse_geometry(char const* str,
+ GError** error)
+ {
+ g_free(geometry);
+ geometry = g_strdup(str);
+ return true;
+ }
+
+ static gboolean
+ parse_cjk_width(char const* option, char const* value, void* data, GError** error)
+ {
+ Options* that = static_cast<Options*>(data);
+ return that->parse_width_enum(value, that->cjk_ambiguous_width, error);
+ };
+
+ static gboolean
+ parse_cursor_blink(char const* option, char const* value, void* data, GError** error)
+ {
+ Options* that = static_cast<Options*>(data);
+ int v;
+ auto rv = that->parse_enum(VTE_TYPE_CURSOR_BLINK_MODE, value, &v, error);
+ if (rv)
+ that->cursor_blink_mode = VteCursorBlinkMode(v);
+ return rv;
+ }
+
+ static gboolean
+ parse_cursor_shape(char const* option, char const* value, void* data, GError** error)
+ {
+ Options* that = static_cast<Options*>(data);
+ int v;
+ auto rv = that->parse_enum(VTE_TYPE_CURSOR_SHAPE, value, &v, error);
+ if (rv)
+ that->cursor_shape = VteCursorShape(v);
+ return rv;
+ }
+
+ static gboolean
+ parse_cursor_bg_color(char const* option, char const* value, void* data, GError** error)
+ {
+ Options* that = static_cast<Options*>(data);
+ return that->parse_color(value, &that->cursor_bg_color, &that->cursor_bg_color_set, error);
+ }
+
+ static gboolean
+ parse_cursor_fg_color(char const* option, char const* value, void* data, GError** error)
+ {
+ Options* that = static_cast<Options*>(data);
+ return that->parse_color(value, &that->cursor_fg_color, &that->cursor_fg_color_set, error);
+ }
+
+ static gboolean
+ parse_hl_bg_color(char const* option, char const* value, void* data, GError** error)
+ {
+ Options* that = static_cast<Options*>(data);
+ return that->parse_color(value, &that->hl_bg_color, &that->hl_bg_color_set, error);
+ }
+
+ static gboolean
+ parse_hl_fg_color(char const* option, char const* value, void* data, GError** error)
+ {
+ Options* that = static_cast<Options*>(data);
+ return that->parse_color(value, &that->hl_fg_color, &that->hl_fg_color_set, error);
+ }
+
+ static gboolean
+ parse_verbosity(char const* option, char const* value, void* data, GError** error)
+ {
+ Options* that = static_cast<Options*>(data);
+ that->verbosity++;
+ return true;
+ }
+
+public:
+
+ GdkRGBA get_color_bg()
+ {
+ double alpha = double(100 - CLAMP(transparency_percent, 0, 100)) / 100.0;
+
+ if (reverse)
+ return GdkRGBA{1.0, 1.0, 1.0, alpha};
+ else
+ return GdkRGBA{0.0, 0.0, 0.0, alpha};
+ }
+
+ GdkRGBA get_color_fg()
+ {
+ if (reverse)
+ return GdkRGBA{0.0, 0.0, 0.0, 1.0};
+ else
+ return GdkRGBA{1.0, 1.0, 1.0, 1.0};
+ }
+
+ bool parse_argv(int argc,
+ char* argv[],
+ GError** error)
+ {
+ bool dummy_bool;
+ char* dummy_string = nullptr;
+ GOptionEntry const entries[] = {
+ { "allow-window-ops", 0, 0, G_OPTION_ARG_NONE, &allow_window_ops,
+ "Allow window operations (resize, move, raise/lower, (de)iconify)", nullptr },
+ { "audible-bell", 'a', 0, G_OPTION_ARG_NONE, &audible_bell,
+ "Use audible terminal bell", nullptr },
+ { "cjk-width", 0, 0, G_OPTION_ARG_CALLBACK, (void*)parse_cjk_width,
+ "Specify the cjk ambiguous width to use for UTF-8 encoding", "NARROW|WIDE" },
+ { "cursor-blink", 0, 0, G_OPTION_ARG_CALLBACK, (void*)parse_cursor_blink,
+ "Cursor blink mode (system|on|off)", "MODE" },
+ { "cursor-background-color", 0, 0, G_OPTION_ARG_CALLBACK,
(void*)parse_cursor_bg_color,
+ "Enable a colored cursor background", nullptr },
+ { "cursor-foreground-color", 0, 0, G_OPTION_ARG_CALLBACK,
(void*)parse_cursor_fg_color,
+ "Enable a colored cursor foreground", nullptr },
+ { "cursor-shape", 0, 0, G_OPTION_ARG_CALLBACK, (void*)parse_cursor_shape,
+ "Set cursor shape (block|underline|ibeam)", nullptr },
+ { "dingu", 'D', 0, G_OPTION_ARG_STRING_ARRAY, &dingus,
+ "Add regex highlight", nullptr },
+ { "debug", 'd', 0,G_OPTION_ARG_NONE, &debug,
+ "Enable various debugging checks", nullptr },
+ { "encoding", 0, 0, G_OPTION_ARG_STRING, &encoding,
+ "Specify the terminal encoding to use", nullptr },
+ { "env", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &environment,
+ "Add environment variable to the child\'s environment", "VAR=VALUE" },
+ { "extra-margin", 0, 0, G_OPTION_ARG_INT, &extra_margin,
+ "Add extra margin around the terminal widget", "MARGIN" },
+ { "font", 'f', 0, G_OPTION_ARG_STRING, &font_string,
+ "Specify a font to use", nullptr },
+ { "gregex", 0, 0, G_OPTION_ARG_NONE, &use_gregex,
+ "Use GRegex instead of PCRE2", nullptr },
+ { "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry,
+ "Set the size (in characters) and position", "GEOMETRY" },
+ { "highlight-background-color", 0, 0, G_OPTION_ARG_CALLBACK,
(void*)parse_hl_bg_color,
+ "Enable distinct highlight background color for selection", nullptr },
+ { "highlight-foreground-color", 0, 0, G_OPTION_ARG_CALLBACK,
(void*)parse_hl_fg_color,
+ "Enable distinct highlight foreground color for selection", nullptr },
+ { "icon-title", 'i', 0, G_OPTION_ARG_NONE, &icon_title,
+ "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-builtin-dingus", 0, 0, G_OPTION_ARG_NONE, &no_builtin_dingus,
+ "Highlight URLs inside the terminal", nullptr },
+ { "no-context-menu", 0, 0, G_OPTION_ARG_NONE, &no_context_menu,
+ "Disable context menu", nullptr },
+ { "no-double-buffer", '2', 0, G_OPTION_ARG_NONE, &no_double_buffer,
+ "Disable double-buffering", nullptr },
+ { "no-geometry-hints", 'G', 0, G_OPTION_ARG_NONE, &no_geometry_hints,
+ "Allow the terminal to be resized to any dimension, not constrained to fit to an
integer multiple of characters", nullptr },
+ { "no-hyperlink", 'H', 0, G_OPTION_ARG_NONE, &no_hyperlink,
+ "Disable hyperlinks", nullptr },
+ { "no-rewrap", 'R', 0, G_OPTION_ARG_NONE, &no_rewrap,
+ "Disable rewrapping on resize", nullptr },
+ { "no-shell", 'S', 0, G_OPTION_ARG_NONE, &no_shell,
+ "Disable spawning a shell inside the terminal", nullptr },
+ { "object-notifications", 'N', 0, G_OPTION_ARG_NONE, &object_notifications,
+ "Print VteTerminal object notifications", nullptr },
+ { "output-file", 0, 0, G_OPTION_ARG_FILENAME, &output_filename,
+ "Save terminal contents to file at exit", nullptr },
+ { "reverse", 0, 0, G_OPTION_ARG_NONE, &reverse,
+ "Reverse foreground/background colors", nullptr },
+ { "scrollback-lines", 'n', 0, G_OPTION_ARG_INT, &scrollback_lines,
+ "Specify the number of scrollback-lines (-1 for infinite)", nullptr },
+ { "transparent", 'T', 0, G_OPTION_ARG_INT, &transparency_percent,
+ "Enable the use of a transparent background", "0..100" },
+ { "verbose", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
+ (void*)parse_verbosity,
+ "Enable verbose debug output", nullptr },
+ { "version", 0, 0, G_OPTION_ARG_NONE, &version,
+ "Show version", nullptr },
+ { "word-char-exceptions", 0, 0, G_OPTION_ARG_STRING, &word_char_exceptions,
+ "Specify the word char exceptions", "CHARS" },
+ { "working-directory", 'w', 0, G_OPTION_ARG_FILENAME, &working_directory,
+ "Specify the initial working directory of the terminal", nullptr },
+
+ /* Options for compatibility with the old vteapp test application */
+ { "border-width", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &extra_margin,
+ nullptr, nullptr },
+ { "command", 'c', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &command,
+ 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,
+ &dummy_string, nullptr, nullptr },
+ { "scrolled-window", 'W', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
+ &dummy_bool, nullptr, nullptr },
+ { "shell", 'S', G_OPTION_FLAG_REVERSE | G_OPTION_FLAG_HIDDEN,
+ G_OPTION_ARG_NONE, &no_shell, nullptr, nullptr },
+
+ { nullptr }
+ };
+
+ /* Look for '--' */
+ for (int i = 0; i < argc; i++) {
+ if (!g_str_equal(argv[i], "--"))
+ continue;
+
+ i++; /* skip it */
+ exec_argv = g_new(char*, argc - i + 1);
+ int j;
+ for (j = 0; i < argc; i++, j++)
+ exec_argv[j] = g_strdup(argv[i]);
+ exec_argv[j] = nullptr;
+ break;
+ }
+
+ auto context = g_option_context_new("[-- COMMAND …] — VTE test application");
+ g_option_context_set_help_enabled(context, true);
+ g_option_context_set_translation_domain(context, GETTEXT_PACKAGE);
+
+ auto group = g_option_group_new(nullptr, nullptr, nullptr, this, nullptr);
+ g_option_group_set_translation_domain(group, GETTEXT_PACKAGE);
+ g_option_group_add_entries(group, entries);
+ g_option_context_set_main_group(context, group);
+
+ g_option_context_add_group(context, gtk_get_option_group(true));
+
+ bool rv = g_option_context_parse(context, &argc, &argv, error);
+
+ g_option_context_free(context);
+ g_free(dummy_string);
+ return rv;
+ }
+};
+
+Options options{}; /* global */
+
+/* debug output */
+
+static void G_GNUC_PRINTF(2, 3)
+verbose_fprintf(FILE* fp,
+ char const* format,
+ ...)
+{
+ if (options.verbosity == 0)
+ return;
+
+ va_list args;
+ va_start(args, format);
+ g_vfprintf(fp, format, args);
+ va_end(args);
+}
+
+#define verbose_print(...) verbose_fprintf(stdout, __VA_ARGS__)
+#define verbose_printerr(...) verbose_fprintf(stderr, __VA_ARGS__)
+
+/* regex */
+
+static GRegex*
+compile_gregex(char const* pattern,
+ bool caseless,
+ GError** error)
+{
+ GRegexCompileFlags flags = GRegexCompileFlags(G_REGEX_OPTIMIZE |
+ G_REGEX_MULTILINE |
+ caseless ? G_REGEX_CASELESS : 0);
+
+ return g_regex_new(pattern, flags, GRegexMatchFlags(0), error);
+}
+
+static void
+jit_regex(VteRegex* regex,
+ char const* pattern)
+{
+ GError *err = nullptr;
+ if (!vte_regex_jit(regex, PCRE2_JIT_COMPLETE, &err) ||
+ !vte_regex_jit(regex, PCRE2_JIT_PARTIAL_SOFT, &err)) {
+ if (!g_error_matches(err, VTE_REGEX_ERROR, -45 /* PCRE2_ERROR_JIT_BADOPTION: JIT not
supported */))
+ verbose_printerr("JITing regex \"%s\" failed: %s\n", pattern, err->message);
+ g_error_free(err);
+ }
+}
+
+static VteRegex*
+compile_regex_for_search(char const* pattern,
+ bool caseless,
+ GError** error)
+{
+ uint32_t flags = PCRE2_UTF | PCRE2_NO_UTF_CHECK | PCRE2_MULTILINE;
+ if (caseless)
+ flags |= PCRE2_CASELESS;
+
+ auto regex = vte_regex_new_for_search(pattern, strlen(pattern), flags, error);
+ if (regex !=nullptr)
+ jit_regex(regex, pattern);
+
+ return regex;
+}
+
+static VteRegex*
+compile_regex_for_match(char const* pattern,
+ bool caseless,
+ GError** error)
+{
+ uint32_t flags = PCRE2_UTF | PCRE2_NO_UTF_CHECK | PCRE2_MULTILINE;
+ if (caseless)
+ flags |= PCRE2_CASELESS;
+
+ auto regex = vte_regex_new_for_match(pattern, strlen(pattern), flags, error);
+ if (regex !=nullptr)
+ jit_regex(regex, pattern);
+
+ return regex;
+}
+
+/* search popover */
+
+#define VTEAPP_TYPE_SEARCH_POPOVER (vteapp_search_popover_get_type())
+#define VTEAPP_SEARCH_POPOVER(o) (G_TYPE_CHECK_INSTANCE_CAST((o), VTEAPP_TYPE_SEARCH_POPOVER,
VteappSearchPopover))
+#define VTEAPP_SEARCH_POPOVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), VTEAPP_TYPE_SEARCH_POPOVER,
VteappSearchPopoverClass))
+#define VTEAPP_IS_SEARCH_POPOVER(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), VTEAPP_TYPE_SEARCH_POPOVER))
+#define VTEAPP_IS_SEARCH_POPOVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), VTEAPP_TYPE_SEARCH_POPOVER))
+#define VTEAPP_SEARCH_POPOVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), VTEAPP_TYPE_SEARCH_POPOVER,
VteappSearchPopoverClass))
+
+typedef struct _VteappSearchPopover VteappSearchPopover;
+typedef struct _VteappSearchPopoverClass VteappSearchPopoverClass;
+
+struct _VteappSearchPopover {
+ GtkPopover parent;
+
+ /* from GtkWidget template */
+ GtkWidget* search_entry;
+ GtkWidget* search_prev_button;
+ GtkWidget* search_next_button;
+ GtkWidget* close_button;
+ GtkToggleButton* match_case_checkbutton;
+ GtkToggleButton* entire_word_checkbutton;
+ GtkToggleButton* regex_checkbutton;
+ GtkToggleButton* wrap_around_checkbutton;
+ GtkWidget* reveal_button;
+ GtkWidget* revealer;
+ /* end */
+
+ VteTerminal *terminal;
+ bool regex_caseless;
+ bool has_regex;
+ char* regex_pattern;
+};
+
+struct _VteappSearchPopoverClass {
+ GtkPopoverClass parent;
+};
+
+static GType vteapp_search_popover_get_type(void);
+
+enum {
+ SEARCH_POPOVER_PROP0,
+ SEARCH_POPOVER_PROP_TERMINAL,
+ SEARCH_POPOVER_N_PROPS
+};
+
+static GParamSpec* search_popover_pspecs[SEARCH_POPOVER_N_PROPS];
+
+static void
+vteapp_search_popover_update_sensitivity(VteappSearchPopover* popover)
+{
+ bool can_search = popover->has_regex;
+
+ gtk_widget_set_sensitive(popover->search_next_button, can_search);
+ gtk_widget_set_sensitive(popover->search_prev_button, can_search);
+}
+
+static void
+vteapp_search_popover_update_regex(VteappSearchPopover* popover)
+{
+ char const* search_text = gtk_entry_get_text(GTK_ENTRY(popover->search_entry));
+ bool caseless = gtk_toggle_button_get_active(popover->match_case_checkbutton) == FALSE;
+
+ char* pattern;
+ if (gtk_toggle_button_get_active(popover->regex_checkbutton))
+ pattern = g_strdup(search_text);
+ else
+ pattern = g_regex_escape_string(search_text, -1);
+
+ if (gtk_toggle_button_get_active(popover->regex_checkbutton)) {
+ char* tmp = g_strdup_printf("\\b%s\\b", pattern);
+ g_free(pattern);
+ pattern = tmp;
+ }
+
+ if (caseless == popover->regex_caseless &&
+ g_strcmp0(pattern, popover->regex_pattern) == 0)
+ return;
+
+ popover->regex_caseless = caseless;
+ g_free(popover->regex_pattern);
+ popover->regex_pattern = nullptr;
+
+ if (search_text[0] != '\0') {
+ GError* error = nullptr;
+
+ if (options.use_gregex) {
+ auto regex = compile_gregex(pattern, caseless, &error);
+ vte_terminal_search_set_gregex(popover->terminal, regex, GRegexMatchFlags(0));
+ if (regex != nullptr)
+ g_regex_unref(regex);
+ } else {
+ auto regex = compile_regex_for_search(pattern, caseless, &error);
+ vte_terminal_search_set_regex(popover->terminal, regex, 0);
+ if (regex != nullptr)
+ vte_regex_unref(regex);
+ }
+
+ if (error == nullptr) {
+ popover->has_regex = true;
+ popover->regex_pattern = pattern; /* adopt */
+ pattern = nullptr; /* adopted */
+ gtk_widget_set_tooltip_text(popover->search_entry, nullptr);
+ } else {
+ popover->has_regex = false;
+ gtk_widget_set_tooltip_text(popover->search_entry, error->message);
+ g_error_free(error);
+ }
+ }
+
+ g_free(pattern);
+
+ vteapp_search_popover_update_sensitivity(popover);
+}
+
+static void
+vteapp_search_popover_wrap_around_toggled(GtkToggleButton* button,
+ VteappSearchPopover* popover)
+{
+ vte_terminal_search_set_wrap_around(popover->terminal, gtk_toggle_button_get_active(button));
+}
+
+static void
+vteapp_search_popover_search_forward(VteappSearchPopover* popover)
+{
+ if (!popover->has_regex)
+ return;
+ vte_terminal_search_find_next(popover->terminal);
+}
+
+static void
+vteapp_search_popover_search_backward(VteappSearchPopover* popover)
+{
+ if (!popover->has_regex)
+ return;
+ vte_terminal_search_find_previous(popover->terminal);
+}
+
+G_DEFINE_TYPE(VteappSearchPopover, vteapp_search_popover, GTK_TYPE_POPOVER)
+
+static void
+vteapp_search_popover_init(VteappSearchPopover* popover)
+{
+ gtk_widget_init_template(GTK_WIDGET(popover));
+
+ popover->regex_caseless = false;
+ popover->has_regex = false;
+ popover->regex_pattern = nullptr;
+
+ g_signal_connect_swapped(popover->close_button, "clicked", G_CALLBACK(gtk_widget_hide), popover);
+
+ g_object_bind_property(popover->reveal_button, "active",
+ popover->revealer, "reveal-child",
+ GBindingFlags(G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE));
+
+#if GTK_CHECK_VERSION(3, 16, 0)
+ g_signal_connect_swapped(popover->search_entry, "next-match",
G_CALLBACK(vteapp_search_popover_search_forward), popover);
+ g_signal_connect_swapped(popover->search_entry, "previous-match",
G_CALLBACK(vteapp_search_popover_search_backward), popover);
+#endif
+ g_signal_connect_swapped(popover->search_entry, "search-changed",
G_CALLBACK(vteapp_search_popover_update_regex), popover);
+
+ g_signal_connect_swapped(popover->search_next_button,"clicked",
G_CALLBACK(vteapp_search_popover_search_forward), popover);
+ g_signal_connect_swapped(popover->search_prev_button,"clicked",
G_CALLBACK(vteapp_search_popover_search_backward), popover);
+
+ g_signal_connect_swapped(popover->match_case_checkbutton,"toggled",
G_CALLBACK(vteapp_search_popover_update_regex), popover);
+ g_signal_connect_swapped(popover->entire_word_checkbutton,"toggled",
G_CALLBACK(vteapp_search_popover_update_regex), popover);
+ g_signal_connect_swapped(popover->regex_checkbutton,"toggled",
G_CALLBACK(vteapp_search_popover_update_regex), popover);
+ g_signal_connect_swapped(popover->match_case_checkbutton, "toggled",
G_CALLBACK(vteapp_search_popover_update_regex), popover);
+
+ g_signal_connect(popover->wrap_around_checkbutton, "toggled",
G_CALLBACK(vteapp_search_popover_wrap_around_toggled), popover);
+
+ vteapp_search_popover_update_sensitivity(popover);
+}
+
+static void
+vteapp_search_popover_finalize(GObject *object)
+{
+ VteappSearchPopover* popover = VTEAPP_SEARCH_POPOVER(object);
+
+ g_free(popover->regex_pattern);
+
+ G_OBJECT_CLASS(vteapp_search_popover_parent_class)->finalize(object);
+}
+
+static void
+vteapp_search_popover_set_property(GObject* object,
+ guint prop_id,
+ const GValue* value,
+ GParamSpec* pspec)
+{
+ VteappSearchPopover* popover = VTEAPP_SEARCH_POPOVER(object);
+
+ switch (prop_id) {
+ case SEARCH_POPOVER_PROP_TERMINAL:
+ popover->terminal = reinterpret_cast<VteTerminal*>(g_value_get_object(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+vteapp_search_popover_class_init(VteappSearchPopoverClass* klass)
+{
+ GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
+ gobject_class->finalize = vteapp_search_popover_finalize;
+ gobject_class->set_property = vteapp_search_popover_set_property;
+
+ search_popover_pspecs[SEARCH_POPOVER_PROP_TERMINAL] =
+ g_param_spec_object("terminal", nullptr, nullptr,
+ VTE_TYPE_TERMINAL,
+ GParamFlags(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS |
G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_properties(gobject_class, G_N_ELEMENTS(search_popover_pspecs),
search_popover_pspecs);
+
+ GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
+ gtk_widget_class_set_template_from_resource(widget_class, "/org/gnome/vte/app/ui/search-popover.ui");
+ gtk_widget_class_bind_template_child(widget_class, VteappSearchPopover, search_entry);
+ gtk_widget_class_bind_template_child(widget_class, VteappSearchPopover, search_prev_button);
+ gtk_widget_class_bind_template_child(widget_class, VteappSearchPopover, search_next_button);
+ gtk_widget_class_bind_template_child(widget_class, VteappSearchPopover, reveal_button);
+ gtk_widget_class_bind_template_child(widget_class, VteappSearchPopover, close_button);
+ gtk_widget_class_bind_template_child(widget_class, VteappSearchPopover, revealer);
+ gtk_widget_class_bind_template_child(widget_class, VteappSearchPopover, match_case_checkbutton);
+ gtk_widget_class_bind_template_child(widget_class, VteappSearchPopover, entire_word_checkbutton);
+ gtk_widget_class_bind_template_child(widget_class, VteappSearchPopover, regex_checkbutton);
+ gtk_widget_class_bind_template_child(widget_class, VteappSearchPopover, wrap_around_checkbutton);
+}
+
+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));
+}
+
+/* terminal */
+
+#define VTEAPP_TYPE_TERMINAL (vteapp_terminal_get_type())
+#define VTEAPP_TERMINAL(o) (G_TYPE_CHECK_INSTANCE_CAST((o), VTEAPP_TYPE_TERMINAL, VteappTerminal))
+#define VTEAPP_TERMINAL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), VTEAPP_TYPE_TERMINAL,
VteappTerminalClass))
+#define VTEAPP_IS_TERMINAL(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), VTEAPP_TYPE_TERMINAL))
+#define VTEAPP_IS_TERMINAL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), VTEAPP_TYPE_TERMINAL))
+#define VTEAPP_TERMINAL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), VTEAPP_TYPE_TERMINAL,
VteappTerminalClass))
+
+typedef struct _VteappTerminal VteappTerminal;
+typedef struct _VteappTerminalClass VteappTerminalClass;
+
+struct _VteappTerminal {
+ VteTerminal parent;
+};
+
+struct _VteappTerminalClass {
+ VteTerminalClass parent;
+};
+
+static GType vteapp_terminal_get_type(void);
+
+G_DEFINE_TYPE(VteappTerminal, vteapp_terminal, VTE_TYPE_TERMINAL)
+
+static void
+vteapp_terminal_class_init(VteappTerminalClass *klass)
+{
+}
+
+static void
+vteapp_terminal_init(VteappTerminal *terminal)
+{
+}
+
+static GtkWidget *
+vteapp_terminal_new(void)
+{
+ return reinterpret_cast<GtkWidget*>(g_object_new(VTEAPP_TYPE_TERMINAL, nullptr));
+}
+
+/* terminal window */
+
+#define VTEAPP_TYPE_WINDOW (vteapp_window_get_type())
+#define VTEAPP_WINDOW(o) (G_TYPE_CHECK_INSTANCE_CAST((o), VTEAPP_TYPE_WINDOW, VteappWindow))
+#define VTEAPP_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), VTEAPP_TYPE_WINDOW, VteappWindowClass))
+#define VTEAPP_IS_WINDOW(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), VTEAPP_TYPE_WINDOW))
+#define VTEAPP_IS_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), VTEAPP_TYPE_WINDOW))
+#define VTEAPP_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), VTEAPP_TYPE_WINDOW, VteappWindowClass))
+
+typedef struct _VteappWindow VteappWindow;
+typedef struct _VteappWindowClass VteappWindowClass;
+
+struct _VteappWindow {
+ GtkApplicationWindow parent;
+
+ /* from GtkWidget template */
+ GtkWidget* window_box;
+ GtkScrollbar* scrollbar;
+ /* GtkBox* notifications_box; */
+ GtkWidget* readonly_emblem;
+ /* GtkButton* copy_button; */
+ /* GtkButton* paste_button; */
+ GtkToggleButton* find_button;
+ GtkMenuButton* gear_button;
+ /* end */
+
+ VteTerminal* terminal;
+ GtkClipboard* clipboard;
+ GPid child_pid;
+ GtkWidget* search_popover;
+
+ /* used for updating the geometry hints */
+ int cached_char_width{0};
+ int cached_char_height{0};
+ int cached_chrome_width{0};
+ int cached_chrome_height{0};
+ int cached_csd_width{0};
+ int cached_csd_height{0};
+};
+
+struct _VteappWindowClass {
+ GtkApplicationWindowClass parent;
+};
+
+static GType vteapp_window_get_type(void);
+
+static char const* const builtin_dingus[] = {
+
"(((gopher|news|telnet|nntp|file|http|ftp|https)://)|(www|ftp)[-A-Za-z0-9]*\\.)[-A-Za-z0-9\\.]+(:[0-9]*)?",
+
"(((gopher|news|telnet|nntp|file|http|ftp|https)://)|(www|ftp)[-A-Za-z0-9]*\\.)[-A-Za-z0-9\\.]+(:[0-9]*)?/[-A-Za-z0-9_\\$\\.\\+\\!\\*\\(\\),;:@&=\\?/~\\#\\%]*[^]'\\.}>\\)
,\\\"]",
+ nullptr,
+};
+
+/* Just some arbitrary minimum values */
+#define MIN_COLUMNS (16)
+#define MIN_ROWS (2)
+
+static void
+vteapp_window_add_dingus(VteappWindow* window,
+ char const* const* dingus)
+{
+ for (auto i = 0; dingus[i] != nullptr; i++) {
+ int tag = -1;
+ GError* error = nullptr;
+ if (options.use_gregex) {
+ auto regex = compile_gregex(dingus[i], true, &error);
+ if (regex) {
+ tag = vte_terminal_match_add_gregex(window->terminal, regex,
+ GRegexMatchFlags(0));
+ g_regex_unref(regex);
+ }
+ } else {
+ auto regex = compile_regex_for_match(dingus[i], true, &error);
+ if (regex) {
+ tag = vte_terminal_match_add_regex(window->terminal, regex, 0);
+ vte_regex_unref(regex);
+ }
+ }
+
+ if (error != nullptr) {
+ verbose_printerr("Failed to compile regex \"%s\": %s\n", dingus[i], error->message);
+ g_error_free(error);
+ }
+
+ if (tag != -1)
+ vte_terminal_match_set_cursor_name(window->terminal, tag, "pointer");
+ }
+}
+
+static void
+vteapp_window_update_geometry(VteappWindow* window)
+{
+ if (options.no_geometry_hints)
+ return;
+
+ GtkWidget* window_widget = GTK_WIDGET(window);
+ GtkWidget* terminal_widget = GTK_WIDGET(window->terminal);
+
+ int csd_width = 0;
+ int csd_height = 0;
+ if (gtk_widget_get_realized(terminal_widget)) {
+ /* Calculate the CSD size as difference between the toplevel's
+ * and content's allocation.
+ */
+ GtkAllocation toplevel, contents;
+ gtk_widget_get_allocation(window_widget, &toplevel);
+ gtk_widget_get_allocation(window->window_box, &contents);
+
+ csd_width = toplevel.width - contents.width;
+ csd_height = toplevel.height - contents.height;
+ g_assert_cmpint(csd_width, >=, 0);
+ g_assert_cmpint(csd_height, >=, 0);
+ }
+
+ int columns = vte_terminal_get_column_count(window->terminal);
+ int rows = vte_terminal_get_row_count(window->terminal);
+ int char_width = vte_terminal_get_char_width(window->terminal);
+ int char_height = vte_terminal_get_char_height(window->terminal);
+
+ /* Calculate the chrome size as difference between the content's
+ * natural size requisition and the terminal grid's size.
+ * This includes the terminal's padding in the chrome.
+ */
+ GtkRequisition contents_req;
+ gtk_widget_get_preferred_size(window->window_box, nullptr, &contents_req);
+ int chrome_width = contents_req.width - char_width * columns;
+ int chrome_height = contents_req.height - char_height * rows;
+ g_assert_cmpint(chrome_width, >=, 0);
+ g_assert_cmpint(chrome_height, >=, 0);
+
+ /* Only actually set the geometry hints once the window is realized, since
+ * only then we know the CSD size. Only set the geometry when anything
+ * has changed.
+ */
+ if (gtk_widget_get_realized(terminal_widget) &&
+ (char_height != window->cached_char_height ||
+ char_width != window->cached_char_width ||
+ chrome_width != window->cached_chrome_width ||
+ chrome_height != window->cached_chrome_height ||
+ csd_width != window->cached_csd_width ||
+ csd_width != window->cached_csd_height)) {
+ GdkGeometry geometry;
+
+ geometry.base_width = csd_width + chrome_width;
+ geometry.base_height = csd_height + chrome_height;
+ geometry.width_inc = char_width;
+ geometry.height_inc = char_height;
+ geometry.min_width = geometry.base_width + char_width * MIN_COLUMNS;
+ geometry.min_height = geometry.base_height + char_height * MIN_ROWS;
+
+ gtk_window_set_geometry_hints(GTK_WINDOW(window),
+#if GTK_CHECK_VERSION (3, 19, 5)
+ nullptr,
+#else
+ terminal_widget,
+#endif
+ &geometry,
+ GdkWindowHints(GDK_HINT_RESIZE_INC |
+ GDK_HINT_MIN_SIZE |
+ GDK_HINT_BASE_SIZE));
+
+ window->cached_csd_width = csd_width;
+ window->cached_csd_height = csd_height;
+ }
+
+ window->cached_char_width = char_width;
+ window->cached_char_height = char_height;
+ window->cached_chrome_width = chrome_width;
+ window->cached_chrome_height = chrome_height;
+}
+
+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_TILED)) != 0)
+ return;
+
+ /* First, update the geometry hints, so that the cached_* members are up-to-date */
+ vteapp_window_update_geometry(window);
+
+ /* Calculate the window's pixel size corresponding to the terminal's grid size */
+ int columns = vte_terminal_get_column_count(window->terminal);
+ int rows = vte_terminal_get_row_count(window->terminal);
+ int pixel_width = window->cached_chrome_width + window->cached_char_width * columns;
+ int pixel_height = window->cached_chrome_height + window->cached_char_height * rows;
+
+ verbose_print("VteappWindow resize grid %dx%d pixel %dx%d\n",
+ columns, rows, pixel_width, pixel_height);
+
+ gtk_window_resize(GTK_WINDOW(window), pixel_width, pixel_height);
+}
+
+static void
+vteapp_window_parse_geometry(VteappWindow* window)
+{
+ /* First update the geometry hints, so that gtk_window_parse_geometry()
+ * knows the char width/height and base size increments.
+ */
+ vteapp_window_update_geometry(window);
+
+ //FIXMEchpe gtk_widget_realize(GTK_WIDGET(window->terminal));
+
+ if (options.geometry != nullptr) {
+ if (gtk_window_parse_geometry(GTK_WINDOW(window), options.geometry)) {
+ /* After parse_geometry(), we can get the default size in
+ * width/height increments, i.e. in grid size.
+ */
+ int columns, rows;
+ gtk_window_get_default_size(GTK_WINDOW(window), &columns, &rows);
+ vte_terminal_set_size(window->terminal, columns, rows);
+ gtk_window_resize_to_geometry(GTK_WINDOW(window), columns, rows);
+ } else
+ verbose_printerr("Failed to parse geometry spec \"%s\"\n", options.geometry);
+ } else {
+ /* In GTK+ 3.0, the default size of a window comes from its minimum
+ * size not its natural size, so we need to set the right default size
+ * explicitly */
+ gtk_window_set_default_geometry(GTK_WINDOW(window),
+ vte_terminal_get_column_count(window->terminal),
+ vte_terminal_get_row_count(window->terminal));
+ }
+}
+
+static void
+vteapp_window_adjust_font_size(VteappWindow* window,
+ double factor)
+{
+ vte_terminal_set_font_scale(window->terminal,
+ vte_terminal_get_font_scale(window->terminal) * factor);
+
+ vteapp_window_resize(window);
+}
+
+static void
+window_spawn_cb(VteTerminal* terminal,
+ GPid child_pid,
+ GError* error,
+ void* data)
+{
+ if (terminal == nullptr) /* terminal destroyed while spawning */
+ return;
+
+ VteappWindow* window = VTEAPP_WINDOW(data);
+ window->child_pid = child_pid;
+
+ if (child_pid != -1)
+ verbose_printerr("Spawning succeded, PID=%ld\n", (long)child_pid);
+
+ if (error != nullptr) {
+ verbose_printerr("Spawning failed: %s\n", error->message);
+
+ if (!options.keep)
+ gtk_widget_destroy(GTK_WIDGET(window));
+ }
+}
+
+static bool
+vteapp_window_launch_argv(VteappWindow* window,
+ char** argv,
+ GError** error)
+{
+ vte_terminal_spawn_async(window->terminal,
+ VTE_PTY_DEFAULT,
+ options.working_directory,
+ argv,
+ options.environment,
+ G_SPAWN_SEARCH_PATH_FROM_ENVP,
+ nullptr, nullptr, nullptr, /* child setup, data and destroy */
+ 30 * 1000 /* 30s timeout */,
+ nullptr /* cancellable */,
+ window_spawn_cb, window);
+ return true;
+}
+
+static bool
+vteapp_window_launch_commandline(VteappWindow* window,
+ char* commandline,
+ GError** error)
+{
+ int argc;
+ char** argv;
+ if (!g_shell_parse_argv(commandline, &argc, &argv, error))
+ return false;
+
+ bool rv = vteapp_window_launch_argv(window, argv, error);
+
+ g_strfreev(argv);
+ return rv;
+}
+
+static bool
+vteapp_window_launch_shell(VteappWindow* window,
+ GError** error)
+{
+ char* shell = vte_get_user_shell();
+ if (shell == nullptr || shell[0] == '\0') {
+ g_free(shell);
+ shell = g_strdup(g_getenv("SHELL"));
+ }
+ if (shell == nullptr || shell[0] == '\0') {
+ g_free(shell);
+ shell = g_strdup("/bin/sh");
+ }
+
+ char* argv[2] = { shell, nullptr };
+
+ bool rv = vteapp_window_launch_argv(window, argv, error);
+
+ g_free(shell);
+ return rv;
+}
+
+static bool
+vteapp_window_fork(VteappWindow* window,
+ GError** error)
+{
+ auto pty = vte_pty_new_sync(VTE_PTY_DEFAULT, nullptr, error);
+ if (pty == nullptr)
+ return false;
+
+ auto pid = fork();
+ switch (pid) {
+ case -1: /* error */
+ g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error forking: %m");
+ return false;
+
+ case 0: /* child */ {
+ vte_pty_child_setup(pty);
+
+ for (auto i = 0; ; i++) {
+ switch (i % 3) {
+ case 0:
+ case 1:
+ g_print("%d\n", i);
+ break;
+ case 2:
+ g_printerr("%d\n", i);
+ break;
+ }
+ g_usleep(G_USEC_PER_SEC);
+ }
+ }
+ default: /* parent */
+ vte_terminal_set_pty(window->terminal, pty);
+ vte_terminal_watch_child(window->terminal, pid);
+ verbose_print("Child PID is %d (mine is %d).\n", (int)pid, (int)getpid());
+ break;
+ }
+
+ g_object_unref(pty);
+ return true;
+}
+
+static void
+vteapp_window_launch(VteappWindow* window)
+{
+ bool rv;
+ GError* err = nullptr;
+
+ if (options.exec_argv != nullptr)
+ rv = vteapp_window_launch_argv(window, options.exec_argv, &err);
+ else if (options.command != nullptr)
+ rv = vteapp_window_launch_commandline(window, options.command, &err);
+ else if (!options.no_shell)
+ rv = vteapp_window_launch_shell(window, &err);
+ else
+ rv = vteapp_window_fork(window, &err);
+
+ if (!rv) {
+ verbose_printerr("Error launching: %s\n", err->message);
+ g_error_free(err);
+ }
+}
+
+static void
+window_update_copy_sensitivity(VteappWindow* window)
+{
+ auto action = g_action_map_lookup_action(G_ACTION_MAP(window), "copy");
+ g_simple_action_set_enabled(G_SIMPLE_ACTION(action),
+ vte_terminal_get_has_selection(window->terminal));
+}
+
+static void
+window_update_paste_sensitivity(VteappWindow* window)
+{
+ 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);
+ }
+
+ auto action = g_action_map_lookup_action(G_ACTION_MAP(window), "copy");
+ g_simple_action_set_enabled(G_SIMPLE_ACTION(action), can_paste);
+}
+
+/* Callbacks */
+
+static void
+window_action_copy_cb(GSimpleAction* action,
+ GVariant* parameter,
+ void* data)
+{
+ VteappWindow* window = VTEAPP_WINDOW(data);
+ char const* str = g_variant_get_string(parameter, nullptr);
+
+ vte_terminal_copy_clipboard_format(window->terminal,
+ g_str_equal(str, "html") ? VTE_FORMAT_HTML : VTE_FORMAT_TEXT);
+
+}
+
+static void
+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);
+ gtk_clipboard_set_text(window->clipboard, str, len);
+}
+
+static void
+window_action_paste_cb(GSimpleAction* action,
+ GVariant* parameter,
+ void* data)
+{
+ VteappWindow* window = VTEAPP_WINDOW(data);
+ vte_terminal_paste_clipboard(window->terminal);
+}
+
+static void
+window_action_reset_cb(GSimpleAction* action,
+ GVariant* parameter,
+ void* data)
+{
+ VteappWindow* window = VTEAPP_WINDOW(data);
+ bool clear;
+ GdkModifierType modifiers;
+
+ if (parameter != nullptr)
+ clear = g_variant_get_boolean(parameter);
+ else if (gtk_get_current_event_state(&modifiers))
+ clear = (modifiers & GDK_CONTROL_MASK) != 0;
+ else
+ clear = false;
+
+ vte_terminal_reset(window->terminal, true, clear);
+}
+
+static void
+window_action_find_cb(GSimpleAction* action,
+ GVariant* parameter,
+ void* data)
+{
+ VteappWindow* window = VTEAPP_WINDOW(data);
+ gtk_toggle_button_set_active(window->find_button, true);
+}
+
+static bool
+vteapp_window_show_context_menu(VteappWindow* window,
+ guint button,
+ guint32 timestamp,
+ GdkEvent* event)
+{
+ if (options.no_context_menu)
+ return false;
+
+ auto menu = g_menu_new();
+ 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);
+ GVariant* target = g_variant_new_string(hyperlink);
+ auto item = g_menu_item_new("Copy _Hyperlink", nullptr);
+ g_menu_item_set_action_and_target_value(item, "win.copy-match", target);
+ g_menu_append_item(menu, item);
+ g_object_unref(item);
+ }
+
+ auto match = vte_terminal_match_check_event(window->terminal, event, nullptr);
+ if (match != nullptr) {
+ verbose_print("Match: %s\n", hyperlink);
+ GVariant* target = g_variant_new_string(hyperlink);
+ auto item = g_menu_item_new("Copy _Match", nullptr);
+ g_menu_item_set_action_and_target_value(item, "win.copy-match", target);
+ g_menu_append_item(menu, item);
+ g_object_unref(item);
+ }
+
+ /* Test extra match API */
+ char* extra_match = nullptr;
+ if (options.use_gregex) {
+ auto regex = compile_gregex("\\d+", false, nullptr);
+ vte_terminal_event_check_gregex_simple(window->terminal, event,
+ ®ex, 1,
+ GRegexMatchFlags(0),
+ &extra_match);
+ g_regex_unref(regex);
+ } else {
+ auto regex = compile_regex_for_match("\\d+", false, nullptr);
+ vte_terminal_event_check_regex_simple(window->terminal, event,
+ ®ex, 1, 0,
+ &extra_match);
+ vte_regex_unref(regex);
+ }
+
+ if (extra_match != nullptr) {
+ verbose_print("\\d+ match: %s\n", extra_match);
+ g_free(extra_match);
+ }
+ }
+
+ g_menu_append(menu, "_Paste", "win.paste");
+
+ auto popup = gtk_menu_new_from_model(G_MENU_MODEL(menu));
+ gtk_menu_attach_to_widget(GTK_MENU(popup), GTK_WIDGET(window->terminal), nullptr);
+ gtk_menu_popup(GTK_MENU(popup), nullptr, nullptr, nullptr, nullptr, button, timestamp);
+ if (button == 0)
+ gtk_menu_shell_select_first(GTK_MENU_SHELL(popup), true);
+
+ return true;
+}
+
+static gboolean
+window_popup_menu_cb(GtkWidget* widget,
+ VteappWindow* window)
+{
+ return vteapp_window_show_context_menu(window, 0, gtk_get_current_event_time(), nullptr);
+}
+
+static gboolean
+window_button_press_cb(GtkWidget* widget,
+ GdkEventButton* event,
+ VteappWindow* window)
+{
+ if (event->button != GDK_BUTTON_SECONDARY)
+ return false;
+
+ return vteapp_window_show_context_menu(window, event->button, event->time,
+ reinterpret_cast<GdkEvent*>(event));
+}
+
+static void
+window_char_size_changed_cb(VteTerminal* term,
+ guint width,
+ guint height,
+ VteappWindow* window)
+{
+ vteapp_window_update_geometry(window);
+}
+
+static void
+window_child_exited_cb(VteTerminal* term,
+ int status,
+ VteappWindow* window)
+{
+ verbose_printerr("Child exited with status %x\n", status);
+
+ if (options.output_filename != nullptr) {
+ auto file = g_file_new_for_commandline_arg(options.output_filename);
+
+ GError* err = nullptr;
+ auto stream = g_file_replace(file, nullptr, false, G_FILE_CREATE_NONE, nullptr, &err);
+ if (stream != nullptr) {
+ vte_terminal_write_contents_sync(window->terminal,
+ G_OUTPUT_STREAM(stream),
+ VTE_WRITE_DEFAULT,
+ nullptr, &err);
+ g_object_unref(stream);
+ }
+
+ if (err != nullptr) {
+ verbose_printerr("Failed to write output to \"%s\": %s\n",
+ options.output_filename, err->message);
+ g_error_free(err);
+ }
+ }
+
+ window->child_pid = -1;
+
+ if (options.keep)
+ return;
+
+ gtk_widget_destroy(GTK_WIDGET(window));
+}
+
+static void
+window_clipboard_owner_change_cb(GtkClipboard* clipboard,
+ GdkEvent* event,
+ VteappWindow* window)
+{
+ window_update_paste_sensitivity(window);
+}
+
+static void
+window_decrease_font_size_cb(VteTerminal* terminal,
+ VteappWindow* window)
+{
+ vteapp_window_adjust_font_size(window, 1.0 / 1.2);
+}
+
+static void
+window_increase_font_size_cb(VteTerminal* terminal,
+ VteappWindow* window)
+{
+ vteapp_window_adjust_font_size(window, 1.2);
+}
+
+static void
+window_deiconify_window_cb(VteTerminal* terminal,
+ VteappWindow* window)
+{
+ if (!options.allow_window_ops)
+ return;
+
+ gtk_window_deiconify(GTK_WINDOW(window));
+}
+
+static void
+window_iconify_window_cb(VteTerminal* terminal,
+ VteappWindow* window)
+{
+ if (!options.allow_window_ops)
+ return;
+
+ 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));
+}
+
+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));
+}
+
+static void
+window_lower_window_cb(VteTerminal* terminal,
+ VteappWindow* window)
+{
+ if (!options.allow_window_ops)
+ return;
+ if (!gtk_widget_get_realized(GTK_WIDGET(window)))
+ return;
+
+ gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window)));
+}
+
+static void
+window_raise_window_cb(VteTerminal* terminal,
+ VteappWindow* window)
+{
+ if (!options.allow_window_ops)
+ return;
+ if (!gtk_widget_get_realized(GTK_WIDGET(window)))
+ return;
+
+ gdk_window_raise(gtk_widget_get_window(GTK_WIDGET(window)));
+}
+
+static void
+window_maximize_window_cb(VteTerminal* terminal,
+ VteappWindow* window)
+{
+ if (!options.allow_window_ops)
+ return;
+
+ gtk_window_maximize(GTK_WINDOW(window));
+}
+
+static void
+window_restore_window_cb(VteTerminal* terminal,
+ VteappWindow* window)
+{
+ if (!options.allow_window_ops)
+ return;
+
+ gtk_window_unmaximize(GTK_WINDOW(window));
+}
+
+static void
+window_move_window_cb(VteTerminal* terminal,
+ guint x,
+ guint y,
+ VteappWindow* window)
+{
+ if (!options.allow_window_ops)
+ return;
+
+ gtk_window_move(GTK_WINDOW(window), x, y);
+}
+
+static void
+window_notify_cb(GObject* object,
+ GParamSpec* pspec,
+ VteappWindow* window)
+{
+ if (pspec->owner_type != VTE_TYPE_TERMINAL)
+ return;
+
+ GValue value G_VALUE_INIT;
+ g_object_get_property(object, pspec->name, &value);
+ auto str = g_strdup_value_contents(&value);
+ g_value_unset(&value);
+
+ verbose_print("NOTIFY property \"%s\" value %s\n", pspec->name, str);
+ g_free(str);
+}
+
+static void
+window_refresh_window_cb(VteTerminal* terminal,
+ VteappWindow* window)
+{
+ gtk_widget_queue_draw(GTK_WIDGET(window));
+}
+
+static void
+window_resize_window_cb(VteTerminal* terminal,
+ guint columns,
+ guint rows,
+ VteappWindow* window)
+{
+ if (!options.allow_window_ops)
+ return;
+ if (columns < MIN_COLUMNS || rows < MIN_ROWS)
+ return;
+
+ vte_terminal_set_size(window->terminal, columns, rows);
+ vteapp_window_resize(window);
+}
+
+static void
+window_selection_changed_cb(VteTerminal* terminal,
+ VteappWindow* window)
+{
+ window_update_copy_sensitivity(window);
+}
+
+static void
+window_input_enabled_state_cb(GAction* action,
+ GParamSpec* pspec,
+ VteappWindow* window)
+{
+ gtk_widget_set_visible(window->readonly_emblem,
+ !g_variant_get_boolean(g_action_get_state(action)));
+}
+
+static void
+window_search_popover_closed_cb(GtkPopover* popover,
+ VteappWindow* window)
+{
+ if (gtk_toggle_button_get_active(window->find_button))
+ gtk_toggle_button_set_active(window->find_button, false);
+}
+
+static void
+window_find_button_toggled_cb(GtkToggleButton* button,
+ VteappWindow* window)
+{
+ auto active = gtk_toggle_button_get_active(button);
+
+ if (gtk_widget_get_visible(GTK_WIDGET(window->search_popover)) != active)
+ gtk_widget_set_visible(GTK_WIDGET(window->search_popover), active);
+}
+
+G_DEFINE_TYPE(VteappWindow, vteapp_window, GTK_TYPE_APPLICATION_WINDOW)
+
+static void
+vteapp_window_init(VteappWindow* window)
+{
+ gtk_widget_init_template(GTK_WIDGET(window));
+
+ window->child_pid = -1;
+}
+
+static void
+vteapp_window_constructed(GObject *object)
+{
+ VteappWindow* window = VTEAPP_WINDOW(object);
+
+ G_OBJECT_CLASS(vteapp_window_parent_class)->constructed(object);
+
+ gtk_window_set_title(GTK_WINDOW(window), "Terminal");
+
+ /* Create terminal and connect scrollbar */
+ window->terminal = reinterpret_cast<VteTerminal*>(vteapp_terminal_new());
+
+ auto margin = options.extra_margin;
+ if (margin >= 0) {
+ gtk_widget_set_margin_start(GTK_WIDGET(window->terminal), margin);
+ gtk_widget_set_margin_end(GTK_WIDGET(window->terminal), margin);
+ gtk_widget_set_margin_top(GTK_WIDGET(window->terminal), margin);
+ 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)));
+
+ /* Create actions */
+ GActionEntry const entries[] = {
+ { "copy", window_action_copy_cb, "s", nullptr, nullptr },
+ { "copy-match", window_action_copy_match_cb, "s", nullptr, nullptr },
+ { "paste", window_action_paste_cb, nullptr, nullptr, nullptr },
+ { "reset", window_action_reset_cb, "b", nullptr, nullptr },
+ { "find", window_action_find_cb, nullptr, nullptr, nullptr },
+ };
+
+ GActionMap* map = G_ACTION_MAP(window);
+ g_action_map_add_action_entries(map, entries, G_N_ELEMENTS(entries), window);
+
+ /* Property actions */
+ auto action = g_property_action_new("input-enabled", window->terminal, "input-enabled");
+ g_action_map_add_action(map, G_ACTION(action));
+ g_object_unref(action);
+ g_signal_connect(action, "notify::state", G_CALLBACK(window_input_enabled_state_cb), window);
+
+ /* 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 */
+ 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);
+
+ /* Set ARGB visual */
+ if (options.transparency_percent >= 0) {
+ 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);
+ }
+
+ /* Signals */
+ 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);
+ g_signal_connect(window->terminal, "char-size-changed", G_CALLBACK(window_char_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);
+ g_signal_connect(window->terminal, "maximize-window", G_CALLBACK(window_maximize_window_cb), window);
+ g_signal_connect(window->terminal, "move-window", G_CALLBACK(window_move_window_cb), window);
+ g_signal_connect(window->terminal, "raise-window", G_CALLBACK(window_raise_window_cb), window);
+ g_signal_connect(window->terminal, "refresh-window", G_CALLBACK(window_refresh_window_cb), window);
+ g_signal_connect(window->terminal, "resize-window", G_CALLBACK(window_resize_window_cb), window);
+ g_signal_connect(window->terminal, "restore-window", G_CALLBACK(window_restore_window_cb), window);
+ g_signal_connect(window->terminal, "selection-changed", G_CALLBACK(window_selection_changed_cb),
window);
+ g_signal_connect(window->terminal, "window-title-changed",
G_CALLBACK(window_window_title_changed_cb), window);
+ if (options.object_notifications)
+ g_signal_connect(window->terminal, "notify", G_CALLBACK(window_notify_cb), window);
+
+ /* Settings */
+ if (options.no_double_buffer)
+ gtk_widget_set_double_buffered(GTK_WIDGET(window->terminal), false);
+
+ if (options.encoding != nullptr) {
+ GError* err = nullptr;
+ if (!vte_terminal_set_encoding(window->terminal, options.encoding, &err)) {
+ verbose_printerr("Failed to set encoding: %s\n", err->message);
+ g_error_free(err);
+ }
+ }
+
+ if (options.word_char_exceptions != nullptr)
+ vte_terminal_set_word_char_exceptions(window->terminal, options.word_char_exceptions);
+
+ vte_terminal_set_allow_hyperlink(window->terminal, !options.no_hyperlink);
+ vte_terminal_set_audible_bell(window->terminal, options.audible_bell);
+ vte_terminal_set_cjk_ambiguous_width(window->terminal, options.cjk_ambiguous_width);
+ vte_terminal_set_cursor_blink_mode(window->terminal, options.cursor_blink_mode);
+ vte_terminal_set_cursor_shape(window->terminal, options.cursor_shape);
+ vte_terminal_set_mouse_autohide(window->terminal, true);
+ vte_terminal_set_rewrap_on_resize(window->terminal, !options.no_rewrap);
+ vte_terminal_set_scroll_on_output(window->terminal, false);
+ vte_terminal_set_scroll_on_keystroke(window->terminal, true);
+ vte_terminal_set_scrollback_lines(window->terminal, options.scrollback_lines);
+
+ /* Style */
+ if (options.font_string != nullptr) {
+ auto desc = pango_font_description_from_string(options.font_string);
+ vte_terminal_set_font(window->terminal, desc);
+ pango_font_description_free(desc);
+ }
+
+ auto fg = options.get_color_fg();
+ auto bg = options.get_color_bg();
+ vte_terminal_set_colors(window->terminal, &fg, &bg, nullptr, 0);
+ if (options.cursor_bg_color_set)
+ vte_terminal_set_color_cursor(window->terminal, &options.cursor_bg_color);
+ if (options.cursor_fg_color_set)
+ vte_terminal_set_color_cursor_foreground(window->terminal, &options.cursor_fg_color);
+ if (options.hl_bg_color_set)
+ vte_terminal_set_color_highlight(window->terminal, &options.hl_bg_color);
+ if (options.hl_fg_color_set)
+ vte_terminal_set_color_highlight_foreground(window->terminal, &options.hl_fg_color);
+
+ /* Dingus */
+ if (!options.no_builtin_dingus)
+ vteapp_window_add_dingus(window, builtin_dingus);
+ if (options.dingus != nullptr)
+ vteapp_window_add_dingus(window, options.dingus);
+
+ /* Done! */
+ gtk_box_pack_start(GTK_BOX(window->window_box), GTK_WIDGET(window->terminal),
+ true, true, 0);
+ gtk_widget_show(GTK_WIDGET(window->terminal));
+
+ window_update_paste_sensitivity(window);
+ window_update_copy_sensitivity(window);
+
+ gtk_widget_grab_focus(GTK_WIDGET(window->terminal));
+
+ /* Sanity check */
+ g_assert(!gtk_widget_get_realized(GTK_WIDGET(window)));
+}
+
+static void
+vteapp_window_dispose(GObject *object)
+{
+ VteappWindow* window = VTEAPP_WINDOW(object);
+
+ if (window->clipboard != nullptr) {
+ g_signal_handlers_disconnect_by_func(window->clipboard,
+ (void*)window_clipboard_owner_change_cb,
+ window);
+ window->clipboard = nullptr;
+ }
+
+ if (window->search_popover != nullptr) {
+ gtk_widget_destroy(window->search_popover);
+ window->search_popover = nullptr;
+ }
+
+ G_OBJECT_CLASS(vteapp_window_parent_class)->dispose(object);
+}
+
+static void
+vteapp_window_realize(GtkWidget* widget)
+{
+ GTK_WIDGET_CLASS(vteapp_window_parent_class)->realize(widget);
+
+ /* Now we can know the CSD size, and thus apply the geometry. */
+ VteappWindow* window = VTEAPP_WINDOW(widget);
+ verbose_print("VteappWindow::realize\n");
+ vteapp_window_resize(window);
+}
+
+static void
+vteapp_window_show(GtkWidget* widget)
+{
+ GTK_WIDGET_CLASS(vteapp_window_parent_class)->show(widget);
+
+ /* Re-apply the geometry. */
+ VteappWindow* window = VTEAPP_WINDOW(widget);
+ verbose_print("VteappWindow::show\n");
+ vteapp_window_resize(window);
+}
+
+static void
+vteapp_window_style_updated(GtkWidget* widget)
+{
+ GTK_WIDGET_CLASS(vteapp_window_parent_class)->style_updated(widget);
+
+ /* Re-apply the geometry. */
+ VteappWindow* window = VTEAPP_WINDOW(widget);
+ verbose_print("VteappWindow::style-update\n");
+ vteapp_window_resize(window);
+}
+
+static void
+vteapp_window_class_init(VteappWindowClass* klass)
+{
+ GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
+ gobject_class->constructed = vteapp_window_constructed;
+ gobject_class->dispose = vteapp_window_dispose;
+
+ GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
+ widget_class->realize = vteapp_window_realize;
+ widget_class->show = vteapp_window_show;
+ widget_class->style_updated = vteapp_window_style_updated;
+
+ gtk_widget_class_set_template_from_resource(widget_class, "/org/gnome/vte/app/ui/window.ui");
+
+ gtk_widget_class_bind_template_child(widget_class, VteappWindow, window_box);
+ gtk_widget_class_bind_template_child(widget_class, VteappWindow, scrollbar);
+ /* gtk_widget_class_bind_template_child(widget_class, VteappWindow, notification_box); */
+ gtk_widget_class_bind_template_child(widget_class, VteappWindow, readonly_emblem);
+ /* gtk_widget_class_bind_template_child(widget_class, VteappWindow, copy_button); */
+ /* gtk_widget_class_bind_template_child(widget_class, VteappWindow, paste_button); */
+ gtk_widget_class_bind_template_child(widget_class, VteappWindow, find_button);
+ gtk_widget_class_bind_template_child(widget_class, VteappWindow, gear_button);
+}
+
+static VteappWindow*
+vteapp_window_new(GApplication* app)
+{
+ return reinterpret_cast<VteappWindow*>(g_object_new(VTEAPP_TYPE_WINDOW,
+ "application", app,
+ nullptr));
+}
+
+/* application */
+
+#define VTEAPP_TYPE_APPLICATION (vteapp_application_get_type())
+#define VTEAPP_APPLICATION(o) (G_TYPE_CHECK_INSTANCE_CAST((o), VTEAPP_TYPE_APPLICATION,
VteappApplication))
+#define VTEAPP_APPLICATION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), VTEAPP_TYPE_APPLICATION,
VteappApplicationClass))
+#define VTEAPP_IS_APPLICATION(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), VTEAPP_TYPE_APPLICATION))
+#define VTEAPP_IS_APPLICATION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), VTEAPP_TYPE_APPLICATION))
+#define VTEAPP_APPLICATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), VTEAPP_TYPE_APPLICATION,
VteappApplicationClass))
+
+typedef struct _VteappApplication VteappApplication;
+typedef struct _VteappApplicationClass VteappApplicationClass;
+
+struct _VteappApplication {
+ GtkApplication parent;
+};
+
+struct _VteappApplicationClass {
+ GtkApplicationClass parent;
+};
+
+static GType vteapp_application_get_type(void);
+
+static void
+app_action_new_cb(GSimpleAction* action,
+ GVariant* parameter,
+ void* data)
+{
+ GApplication* application = G_APPLICATION(data);
+ auto window = vteapp_window_new(application);
+ vteapp_window_parse_geometry(window);
+ gtk_window_present(GTK_WINDOW(window));
+ vteapp_window_launch(window);
+}
+
+static void
+app_action_close_cb(GSimpleAction* action,
+ GVariant* parameter,
+ void* data)
+{
+ GtkApplication* application = GTK_APPLICATION(data);
+ auto window = gtk_application_get_active_window(application);
+ if (window != nullptr)
+ gtk_widget_destroy(GTK_WIDGET(window));
+}
+
+G_DEFINE_TYPE(VteappApplication, vteapp_application, GTK_TYPE_APPLICATION)
+
+static void
+vteapp_application_init(VteappApplication* application)
+{
+ g_object_set(gtk_settings_get_default(),
+ "gtk-enable-mnemonics", FALSE,
+ "gtk-enable-accels", FALSE,
+ /* Make gtk+ CSD not steal F10 from the terminal */
+ "gtk-menu-bar-accel", nullptr,
+ nullptr);
+}
+
+static void
+vteapp_application_startup(GApplication* application)
+{
+ /* Create actions */
+ GActionEntry const entries[] = {
+ { "new", app_action_new_cb, nullptr, nullptr, nullptr },
+ { "close", app_action_close_cb, nullptr, nullptr, nullptr },
+ };
+
+ GActionMap* map = G_ACTION_MAP(application);
+ g_action_map_add_action_entries(map, entries, G_N_ELEMENTS(entries), application);
+
+ g_application_set_resource_base_path (application, "/org/gnome/vte/app");
+
+ G_APPLICATION_CLASS(vteapp_application_parent_class)->startup(application);
+}
+
+static void
+vteapp_application_activate(GApplication* application)
+{
+ auto action = g_action_map_lookup_action(G_ACTION_MAP(application), "new");
+ g_action_activate(action, nullptr);
+}
+
+static void
+vteapp_application_class_init(VteappApplicationClass* klass)
+{
+ GApplicationClass* application_class = G_APPLICATION_CLASS(klass);
+ application_class->startup = vteapp_application_startup;
+ application_class->activate = vteapp_application_activate;
+}
+
+static GApplication*
+vteapp_application_new(void)
+{
+ return reinterpret_cast<GApplication*>(g_object_new(VTEAPP_TYPE_APPLICATION,
+ "application-id", "org.gnome.Vte.Application",
+ "flags", guint(G_APPLICATION_NON_UNIQUE),
+ nullptr));
+}
+
+/* main */
+
+int
+main(int argc,
+ char *argv[])
+{
+ setlocale(LC_ALL, "");
+
+ bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+#ifdef HAVE_DECL_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+#endif
+
+ /* Not interested in silly debug spew, bug #749195 */
+ if (g_getenv("G_ENABLE_DIAGNOSTIC") == nullptr)
+ g_setenv("G_ENABLE_DIAGNOSTIC", "0", true);
+
+ g_set_prgname("Terminal");
+ g_set_application_name("Terminal");
+
+ GError* error = nullptr;
+ if (!options.parse_argv(argc, argv, &error)) {
+ g_printerr("Error parsing arguments: %s\n", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ if (g_getenv("VTE_CJK_WIDTH") != nullptr)
+ verbose_printerr("VTE_CJK_WIDTH is not supported anymore, use --cjk-width instead\n");
+
+ if (options.version) {
+ g_print("VTE Application %s\n", VERSION);
+ return EXIT_SUCCESS;
+ }
+
+ if (options.debug)
+ gdk_window_set_debug_updates(true);
+
+ auto app = vteapp_application_new();
+ auto rv = g_application_run(app, 0, nullptr);
+ g_object_unref(app);
+
+ return rv;
+}
diff --git a/src/app/app.gresource.xml b/src/app/app.gresource.xml
new file mode 100644
index 0000000..4b69e56
--- /dev/null
+++ b/src/app/app.gresource.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright © 2014 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 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope conf 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/>.
+-->
+<gresources>
+ <gresource prefix="/org/gnome/vte/app">
+ <file alias="ui/search-popover.ui" compressed="true"
preprocess="xml-stripblanks">search-popover.ui</file>
+ <file alias="ui/window.ui" compressed="true" preprocess="xml-stripblanks">window.ui</file>
+ <file alias="gtk/menus.ui" compressed="true" preprocess="xml-stripblanks">appmenu.ui</file>
+ </gresource>
+</gresources>
diff --git a/src/app/appmenu.ui b/src/app/appmenu.ui
new file mode 100644
index 0000000..f43a086
--- /dev/null
+++ b/src/app/appmenu.ui
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright © 2017 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 <http://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/search-popover.ui b/src/app/search-popover.ui
new file mode 100644
index 0000000..0ecec70
--- /dev/null
+++ b/src/app/search-popover.ui
@@ -0,0 +1,265 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.19.0 -->
+<!--
+ Copyright © 2016, 2017 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 <http://www.gnu.org/licenses/>.
+-->
+<interface>
+ <requires lib="gtk+" version="3.16"/>
+ <template class="VteappSearchPopover" parent="GtkPopover">
+ <property name="can_focus">False</property>
+ <property name="transitions_enabled">False</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">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="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">18</property>
+ <child>
+ <object class="GtkBox" id="box4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkSearchEntry" id="search_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="activates_default">True</property>
+ <property name="width_chars">30</property>
+ <property name="primary_icon_name">edit-find-symbolic</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">False</property>
+ <property name="placeholder_text" translatable="yes">Search</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="search_prev_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Search for previous
occurrence</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-up-symbolic</property>
+ <property name="use_fallback">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="search_next_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Search for next occurrence</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-down-symbolic</property>
+ <property name="use_fallback">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <style>
+ <class name="linked"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="reveal_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Toggle search options</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">open-menu-symbolic</property>
+ <property name="use_fallback">True</property>
+ </object>
+ </child>
+ <accessibility>
+ <relation type="controller-for" target="revealer"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">window-close-symbolic</property>
+ <property name="use_fallback">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRevealer" id="revealer">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="transition_type">none</property>
+ <child>
+ <object class="GtkBox" id="box3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</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="label" translatable="yes">_Match case</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.49000000953674316</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="entire_word_checkbutton">
+ <property name="label" translatable="yes">Match _entire word only</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="regex_checkbutton">
+ <property name="label" translatable="yes">Match as _regular expression</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="wrap_around_checkbutton">
+ <property name="label" translatable="yes">_Wrap around</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/app/window.ui b/src/app/window.ui
new file mode 100644
index 0000000..e6239c0
--- /dev/null
+++ b/src/app/window.ui
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.19.0 -->
+<!--
+ Copyright © 2014, 2017 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 <http://www.gnu.org/licenses/>.
+-->
+<interface>
+ <requires lib="gtk+" version="3.10"/>
+ <menu id="gear_menu_model">
+ <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>
+ </menu>
+ <template class="VteappWindow" parent="GtkApplicationWindow">
+ <property name="can_focus">False</property>
+ <property name="role">vte-terminal</property>
+ <property name="icon_name">utilities-terminal</property>
+ <child>
+ <object class="GtkBox" id="window_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkScrollbar" id="scrollbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="restrict_to_fill_level">False</property>
+ <property name="fill_level">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="titlebar">
+ <object class="GtkHeaderBar" id="headerbar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="show_close_button">True</property>
+ <property name="decoration_layout">:close</property>
+ <child>
+ <object class="GtkButton" id="copy_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</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">False</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">edit-copy-symbolic</property>
+ <property name="use_fallback">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="paste_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Paste</property>
+ <property name="action_name">win.paste</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">edit-paste-symbolic</property>
+ <property name="use_fallback">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="find_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="image5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">edit-find-symbolic</property>
+ <property name="use_fallback">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child type="title">
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="gear_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="menu-model">gear_menu_model</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">open-menu-symbolic</property>
+ <property name="use_fallback">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="notifications_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkImage" id="readonly_emblem">
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Read-only</property>
+ <property name="icon_name">emblem-readonly</property>
+ <property name="use_fallback">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]