commit c25d9544cc4f4b8f39c2b9a000b5eb3e99cbf565 Author: MSVSphere Packaging Team Date: Tue Nov 26 19:39:33 2024 +0300 import vte291-0.76.3-2.el10 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3515a25 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/vte-0.76.3.tar.xz diff --git a/.vte291.metadata b/.vte291.metadata new file mode 100644 index 0000000..777014f --- /dev/null +++ b/.vte291.metadata @@ -0,0 +1 @@ +1c66e37314b584d12a556a3a22b1b41a42509aae SOURCES/vte-0.76.3.tar.xz diff --git a/SOURCES/0001-a11y-implement-GtkAccessibleText.patch b/SOURCES/0001-a11y-implement-GtkAccessibleText.patch new file mode 100644 index 0000000..4361311 --- /dev/null +++ b/SOURCES/0001-a11y-implement-GtkAccessibleText.patch @@ -0,0 +1,1324 @@ +From aa096f3c96d8f4a736be7fdb48103daffd332296 Mon Sep 17 00:00:00 2001 +From: Christian Hergert +Date: Mon, 4 Mar 2024 14:09:53 -0800 +Subject: [PATCH] a11y: implement GtkAccessibleText + +This is an initial implementaiton of GtkAccessibleText which was added +to GTK for 4.14. It attempts to implement things in a very similar +fashion to the previous code for GTK 3 although considerable effort was +made to simplify and improve readability as to how it works. + +Currently, this supports reading back what you type and what has changed +on screen. It is not yet 1:1 what the GTK 3 a11y implementation did +because ATK was doing many other things (including proxying keyboard +keys) to the other side of the a11y bus. That appears to improve +readback by screen readers in the form of "backspace" and what character +was deleted. + +I expect things to get closer to 1:1 but that work is going to have to +be done inside of GTK itself first and should not require much if +anything here. + +A new VteTerminal:enable-a11y feature flag property has been added +because I'm concerned about enabling this by default until the a11y bus +learns to be more lazy. Currently there is no way to "do nothing" until +a peer (e.g. screenreader) is interested in the contents. + +Ideally, we would have a short-circuit like is currently implemented +by checking vte_terminal_get_enable_a11y() to avoid any sort of +contents calculation when there are no a11y observers. + +It also allows disabling the GTK 3 a11y implementation just to keep +some symmetry between the APIs. + +Currently, this does not implement "text-scrolled" like the GTK 3 +implementation does as I'm not sure yet if there is a benefit. +--- + src/meson.build | 13 +- + src/vte.cc | 11 + + src/vte/vteterminal.h | 7 + + src/vteaccess-gtk4.cc | 874 ++++++++++++++++++++++++++++++++++++++++++ + src/vteaccess-gtk4.h | 25 ++ + src/vteaccess.cc | 21 + + src/vtegtk.cc | 97 ++++- + src/vtegtk.hh | 1 + + src/vteinternal.hh | 8 + + 10 files changed, 1053 insertions(+), 10 deletions(-) + create mode 100644 src/vteaccess-gtk4.cc + create mode 100644 src/vteaccess-gtk4.h + +diff --git a/src/meson.build b/src/meson.build +index 3f89f492..6d1b4b2b 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -18,11 +18,16 @@ subdir('vte') + + src_inc = include_directories('.') + +-a11y_sources = files( ++a11y_gtk3_sources = files( + 'vteaccess.cc', + 'vteaccess.h', + ) + ++a11y_gtk4_sources = files( ++ 'vteaccess-gtk4.cc', ++ 'vteaccess-gtk4.h', ++) ++ + debug_sources = files( + 'debug.cc', + 'debug.h', +@@ -300,7 +305,7 @@ if get_option('gtk3') + libvte_gtk3_public_deps = libvte_common_public_deps + [gtk3_dep,] + + if get_option('a11y') +- libvte_gtk3_sources += a11y_sources ++ libvte_gtk3_sources += a11y_gtk3_sources + endif + + libvte_gtk3 = shared_library( +@@ -349,6 +354,10 @@ if get_option('gtk4') + libvte_gtk4_deps = libvte_common_deps + [gtk4_dep,] + libvte_gtk4_public_deps = libvte_common_public_deps + [gtk4_dep,] + ++ if get_option('a11y') ++ libvte_gtk4_sources += a11y_gtk4_sources ++ endif ++ + libvte_gtk4 = shared_library( + vte_gtk4_api_name, + sources: libvte_gtk4_sources, +diff --git a/src/vte.cc b/src/vte.cc +index a8a0e22c..4d9bf411 100644 +--- a/src/vte.cc ++++ b/src/vte.cc +@@ -10132,6 +10132,17 @@ Terminal::set_text_blink_mode(TextBlinkMode setting) + return true; + } + ++bool ++Terminal::set_enable_a11y(bool setting) ++{ ++ if (setting == m_enable_a11y) ++ return false; ++ ++ m_enable_a11y = setting; ++ ++ return true; ++} ++ + bool + Terminal::set_enable_bidi(bool setting) + { +diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h +index 9c2e2dae..7f4bf6df 100644 +--- a/src/vte/vteterminal.h ++++ b/src/vte/vteterminal.h +@@ -388,6 +388,13 @@ _VTE_PUBLIC + void vte_terminal_set_delete_binding(VteTerminal *terminal, + VteEraseBinding binding) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); + ++/* Accessibility */ ++_VTE_PUBLIC ++void vte_terminal_set_enable_a11y(VteTerminal *terminal, ++ gboolean enable_a11y) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); ++_VTE_PUBLIC ++gboolean vte_terminal_get_enable_a11y(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); ++ + /* BiDi and shaping */ + _VTE_PUBLIC + void vte_terminal_set_enable_bidi(VteTerminal *terminal, +diff --git a/src/vteaccess-gtk4.cc b/src/vteaccess-gtk4.cc +new file mode 100644 +index 00000000..f42e7578 +--- /dev/null ++++ b/src/vteaccess-gtk4.cc +@@ -0,0 +1,874 @@ ++/* ++ * Copyright © 2024 Christian Hergert ++ * Copyright © 2002,2003 Red Hat, Inc. ++ * ++ * 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 . ++ */ ++ ++#include "config.h" ++ ++#include "vteinternal.hh" ++#include "vteaccess-gtk4.h" ++ ++#define GDK_ARRAY_NAME char_positions ++#define GDK_ARRAY_TYPE_NAME CharPositions ++#define GDK_ARRAY_ELEMENT_TYPE int ++#define GDK_ARRAY_BY_VALUE 1 ++#define GDK_ARRAY_PREALLOC 8 ++#define GDK_ARRAY_NO_MEMSET ++#include "gdkarrayimpl.c" ++ ++typedef struct _VteAccessibleTextContents ++{ ++ /* A GdkArrayImpl of attributes per byte */ ++ VteCharAttrList attrs; ++ ++ /* The byte position within the UTF-8 string where each visible ++ * character starts. ++ */ ++ CharPositions characters; ++ ++ /* The character position within the UTF-8 string where each line ++ * break occurs. To get byte offset, use @characters. ++ */ ++ CharPositions linebreaks; ++ ++ /* The UTF-8 string encoded as bytes so that we may reference it ++ * using GBytes for "substrings". However @string does include a ++ * trailing NUL byte in it's size so that the a11y infrastructure ++ * may elide some string copies. ++ */ ++ GBytes *string; ++ ++ /* Number of bytes in @string excluding trailing NUL. */ ++ gsize n_bytes; ++ ++ /* Number of unicode characters in @string. */ ++ gsize n_chars; ++ ++ /* The character position (not bytes) of the caret in @string */ ++ gsize caret; ++ ++ /* Cached column/row of the caret updated each time we are notified ++ * of the caret having moved. We cache this so that we can elide ++ * extraneous notifications after snapshoting. We will update the ++ * carent position synchronously when notified so @caret may always ++ * be relied upon as correct. ++ */ ++ long cached_caret_column; ++ long cached_caret_row; ++} VteAccessibleTextContents; ++ ++typedef struct _VteAccessibleText ++{ ++ VteTerminal *terminal; ++ VteAccessibleTextContents contents[2]; ++ guint contents_flip : 1; ++} VteAccessibleText; ++ ++static inline gboolean ++_pango_color_equal(const PangoColor *a, ++ const PangoColor *b) ++{ ++ return a->red == b->red && ++ a->green == b->green && ++ a->blue == b->blue; ++} ++ ++static void ++vte_accessible_text_contents_init (VteAccessibleTextContents *contents) ++{ ++ vte_char_attr_list_init (&contents->attrs); ++ char_positions_init (&contents->characters); ++ char_positions_init (&contents->linebreaks); ++ contents->string = nullptr; ++ contents->n_bytes = 0; ++ contents->n_chars = 0; ++ contents->caret = 0; ++ contents->cached_caret_row = 0; ++ contents->cached_caret_column = 0; ++} ++ ++static void ++vte_accessible_text_contents_clear (VteAccessibleTextContents *contents) ++{ ++ vte_char_attr_list_clear (&contents->attrs); ++ char_positions_clear (&contents->characters); ++ char_positions_clear (&contents->linebreaks); ++ g_clear_pointer (&contents->string, g_bytes_unref); ++ contents->n_bytes = 0; ++ contents->n_chars = 0; ++ contents->caret = 0; ++ contents->cached_caret_row = 0; ++ contents->cached_caret_column = 0; ++} ++ ++static void ++vte_accessible_text_contents_reset (VteAccessibleTextContents *contents) ++{ ++ vte_char_attr_list_set_size (&contents->attrs, 0); ++ char_positions_set_size (&contents->characters, 0); ++ char_positions_set_size (&contents->linebreaks, 0); ++ g_clear_pointer (&contents->string, g_bytes_unref); ++ contents->n_bytes = 0; ++ contents->n_chars = 0; ++ contents->caret = 0; ++ contents->cached_caret_row = 0; ++ contents->cached_caret_column = 0; ++} ++ ++static const char * ++vte_accessible_text_contents_get_string (VteAccessibleTextContents *contents, ++ gsize *len) ++{ ++ const char *ret; ++ ++ if (contents->string == nullptr || g_bytes_get_size (contents->string) == 0) { ++ *len = 0; ++ return ""; ++ } ++ ++ ret = (const char *)g_bytes_get_data (contents->string, len); ++ ++ if (*len > 0) { ++ (*len)--; ++ } ++ ++ return ret; ++} ++ ++static int ++vte_accessible_text_contents_offset_from_xy (VteAccessibleTextContents *contents, ++ int x, ++ int y) ++{ ++ int offset; ++ int linebreak; ++ int next_linebreak; ++ ++ if (y >= int(char_positions_get_size (&contents->linebreaks))) { ++ y = int(char_positions_get_size (&contents->linebreaks)) - 1; ++ if (y < 0) { ++ return 0; ++ } ++ } ++ ++ linebreak = *char_positions_index (&contents->linebreaks, y); ++ if (y + 1 == int(char_positions_get_size (&contents->linebreaks))) { ++ next_linebreak = int(char_positions_get_size (&contents->characters)); ++ } else { ++ next_linebreak = *char_positions_index (&contents->linebreaks, y + 1); ++ } ++ ++ offset = linebreak + x; ++ if (offset >= next_linebreak) { ++ offset = next_linebreak - 1; ++ } ++ ++ return offset; ++} ++ ++static gunichar ++vte_accessible_text_contents_get_char_at (VteAccessibleTextContents *contents, ++ guint offset) ++{ ++ const char *str; ++ ++ if (contents->string == nullptr) ++ return 0; ++ ++ if (offset >= contents->n_chars) ++ return 0; ++ ++ g_assert (offset < char_positions_get_size (&contents->characters)); ++ ++ str = (const char *)g_bytes_get_data (contents->string, nullptr); ++ str += *char_positions_index (&contents->characters, offset); ++ ++ return g_utf8_get_char (str); ++ ++} ++ ++static GBytes * ++_g_string_free_to_bytes_with_nul (GString *str) ++{ ++ /* g_string_free_to_bytes() will have a trailing-NUL but not include it ++ * in the size of the GBytes. We want the size included in our GBytes ++ * so that GtkAccessibleText may avoid some copies. ++ */ ++ gsize len = str->len + 1; ++ return g_bytes_new_take (g_string_free (str, FALSE), len); ++} ++ ++static inline gsize ++vte_accessible_text_contents_find_caret (VteAccessibleTextContents *contents, ++ long ccol, ++ long crow) ++{ ++ g_assert (contents != nullptr); ++ ++ /* Get the offsets to the beginnings of each line. */ ++ gsize caret = 0; ++ for (gsize i = 0; i < char_positions_get_size (&contents->characters); i++) { ++ /* Get the attributes for the current cell. */ ++ int offset = *char_positions_index (&contents->characters, i); ++ const struct _VteCharAttributes *attrs = vte_char_attr_list_get (&contents->attrs, offset); ++ ++ /* If this cell is "before" the cursor, move the caret to be "here". */ ++ if ((attrs->row < crow) || ++ ((attrs->row == crow) && (attrs->column < ccol))) { ++ caret = i + 1; ++ } ++ } ++ ++ return caret; ++} ++ ++static void ++vte_accessible_text_contents_snapshot (VteAccessibleTextContents *contents, ++ VteTerminal *terminal) ++{ ++ auto impl = _vte_terminal_get_impl (terminal); ++ GString *gstr = g_string_new (nullptr); ++ ++ try { ++ impl->get_text_displayed_a11y (gstr, &contents->attrs); ++ } catch (...) { ++ g_string_truncate (gstr, 0); ++ } ++ ++ if (vte_char_attr_list_get_size (&contents->attrs) >= G_MAXINT) { ++ g_string_truncate (gstr, 0); ++ return; ++ } ++ ++ /* Get the offsets to the beginnings of each character. */ ++ int i = 0; ++ const char *next = gstr->str; ++ int n_attrs = int(vte_char_attr_list_get_size (&contents->attrs)); ++ while (i < n_attrs) { ++ char_positions_append (&contents->characters, &i); ++ next = g_utf8_next_char (next); ++ if (next != nullptr) { ++ i = next - gstr->str; ++ continue; ++ } ++ break; ++ } ++ ++ /* Find offsets for the beginning of lines. */ ++ gsize n_chars = char_positions_get_size (&contents->characters); ++ int row; ++ for (i = 0, row = 0; i < int(n_chars); i++) { ++ /* Get the attributes for the current cell. */ ++ int offset = *char_positions_index (&contents->characters, i); ++ const struct _VteCharAttributes *attrs = vte_char_attr_list_get (&contents->attrs, offset); ++ ++ /* If this character is on a row different from the row ++ * the character we looked at previously was on, then ++ * it's a new line and we need to keep track of where ++ * it is. */ ++ if ((i == 0) || (attrs->row != row)) { ++ _vte_debug_print (VTE_DEBUG_ALLY, ++ "Row %d/%ld begins at %d.\n", ++ int(char_positions_get_size (&contents->linebreaks)), ++ attrs->row, i); ++ char_positions_append (&contents->linebreaks, &i); ++ } ++ ++ row = attrs->row; ++ } ++ ++ /* Add the final line break. */ ++ char_positions_append (&contents->linebreaks, &i); ++ ++ /* Update the caret position. */ ++ long ccol, crow; ++ vte_terminal_get_cursor_position (terminal, &ccol, &crow); ++ _vte_debug_print (VTE_DEBUG_ALLY, "Cursor at (%ld, " "%ld).\n", ccol, crow); ++ gsize caret = vte_accessible_text_contents_find_caret (contents, ccol, crow); ++ ++ contents->n_bytes = gstr->len; ++ contents->n_chars = n_chars; ++ contents->string = _g_string_free_to_bytes_with_nul (gstr); ++ contents->caret = caret; ++ contents->cached_caret_column = ccol; ++ contents->cached_caret_row = crow; ++ ++ _vte_debug_print (VTE_DEBUG_ALLY, ++ "Refreshed accessibility snapshot, " ++ "%ld cells, %ld characters.\n", ++ long(vte_char_attr_list_get_size(&contents->attrs)), ++ long(char_positions_get_size (&contents->characters))); ++} ++ ++static GBytes * ++vte_accessible_text_contents_slice (VteAccessibleTextContents *contents, ++ guint start, ++ guint end) ++{ ++ static const char empty[] = {0}; ++ guint start_offset; ++ guint end_offset; ++ ++ g_assert (contents != nullptr); ++ ++ if (contents->string == nullptr) ++ return g_bytes_new_static (empty, sizeof empty); ++ ++ if (start > contents->n_chars) ++ start = contents->n_chars; ++ ++ if (end > contents->n_chars) ++ end = contents->n_chars; ++ ++ if (end < start) ++ std::swap (end, start); ++ ++ g_assert (start <= char_positions_get_size (&contents->characters)); ++ g_assert (end <= char_positions_get_size (&contents->characters)); ++ ++ if (start == char_positions_get_size (&contents->characters)) ++ start_offset = g_bytes_get_size (contents->string); ++ else ++ start_offset = *char_positions_index (&contents->characters, start); ++ ++ if (end == char_positions_get_size (&contents->characters)) ++ end_offset = g_bytes_get_size (contents->string); ++ else ++ end_offset = *char_positions_index (&contents->characters, end); ++ ++ g_assert (start_offset <= end_offset); ++ ++ if (start_offset == end_offset) ++ return g_bytes_new_static (empty, sizeof empty); ++ ++ return g_bytes_new_from_bytes (contents->string, start_offset, end_offset - start_offset); ++} ++ ++static void ++vte_accessible_text_free (VteAccessibleText *state) ++{ ++ vte_accessible_text_contents_clear (&state->contents[0]); ++ vte_accessible_text_contents_clear (&state->contents[1]); ++ state->terminal = nullptr; ++ g_free (state); ++} ++ ++static VteAccessibleText * ++vte_accessible_text_get (VteTerminal *terminal) ++{ ++ return (VteAccessibleText *)g_object_get_data (G_OBJECT (terminal), "VTE_ACCESSIBLE_TEXT"); ++} ++ ++static GBytes * ++vte_accessible_text_get_contents (GtkAccessibleText *accessible, ++ guint start, ++ guint end) ++{ ++ VteTerminal *terminal = VTE_TERMINAL (accessible); ++ VteAccessibleText *state = vte_accessible_text_get (terminal); ++ VteAccessibleTextContents *contents = nullptr; ++ ++ g_assert (VTE_IS_TERMINAL (terminal)); ++ g_assert (state != nullptr); ++ g_assert (state->terminal == terminal); ++ ++ contents = &state->contents[state->contents_flip]; ++ ++ return vte_accessible_text_contents_slice (contents, start, end); ++} ++ ++static GBytes * ++vte_accessible_text_get_contents_at (GtkAccessibleText *accessible, ++ guint offset, ++ GtkAccessibleTextGranularity granularity, ++ guint *start, ++ guint *end) ++{ ++ VteTerminal *terminal = VTE_TERMINAL (accessible); ++ VteAccessibleText *state = vte_accessible_text_get (terminal); ++ VteAccessibleTextContents *contents; ++ ++ g_assert (VTE_IS_TERMINAL (terminal)); ++ g_assert (state != nullptr); ++ g_assert (state->terminal == terminal); ++ ++ auto impl = _vte_terminal_get_impl (terminal); ++ ++ contents = &state->contents[state->contents_flip]; ++ ++ if (contents->string == nullptr) { ++ return nullptr; ++ } ++ ++ if (offset > contents->n_chars) { ++ offset = contents->n_chars; ++ } ++ ++ switch (granularity) { ++ case GTK_ACCESSIBLE_TEXT_GRANULARITY_CHARACTER: { ++ *start = offset; ++ *end = offset + 1; ++ return vte_accessible_text_contents_slice (contents, offset, offset + 1); ++ } ++ ++ case GTK_ACCESSIBLE_TEXT_GRANULARITY_LINE: { ++ guint char_offset = *char_positions_index (&contents->characters, offset); ++ guint line; ++ ++ for (line = 0; ++ line < char_positions_get_size (&contents->linebreaks); ++ line++) { ++ guint line_offset = *char_positions_index (&contents->linebreaks, line); ++ ++ if (line_offset > char_offset) { ++ line--; ++ break; ++ } ++ } ++ ++ _vte_debug_print (VTE_DEBUG_ALLY, ++ "Character %u is on line %u.\n", ++ offset, line); ++ ++ *start = *char_positions_index (&contents->linebreaks, line); ++ if (line + 1 < char_positions_get_size (&contents->linebreaks)) ++ *end = *char_positions_index (&contents->linebreaks, line + 1); ++ else ++ *end = contents->n_chars; ++ ++ return vte_accessible_text_contents_slice (contents, *start, *end); ++ } ++ ++ case GTK_ACCESSIBLE_TEXT_GRANULARITY_WORD: { ++ gunichar ch = vte_accessible_text_contents_get_char_at (contents, offset); ++ ++ if (ch == 0 || !impl->is_word_char (ch)) ++ break; ++ ++ *start = offset; ++ *end = offset; ++ ++ while (*start > 0 && ++ (ch = vte_accessible_text_contents_get_char_at (contents, *start - 1)) && ++ impl->is_word_char (ch)) { ++ (*start)--; ++ } ++ ++ while (*end < contents->n_chars && ++ (ch = vte_accessible_text_contents_get_char_at (contents, *end + 1)) && ++ impl->is_word_char (ch)) { ++ (*end)++; ++ } ++ ++ return vte_accessible_text_contents_slice (contents, *start, *end); ++ } ++ ++ case GTK_ACCESSIBLE_TEXT_GRANULARITY_SENTENCE: ++ case GTK_ACCESSIBLE_TEXT_GRANULARITY_PARAGRAPH: ++ default: ++ break; ++ } ++ ++ return nullptr; ++} ++ ++static guint ++vte_accessible_text_get_caret_position (GtkAccessibleText *accessible) ++{ ++ VteTerminal *terminal = VTE_TERMINAL (accessible); ++ VteAccessibleText *state = vte_accessible_text_get (terminal); ++ ++ g_assert (VTE_IS_TERMINAL (accessible)); ++ g_assert (state != nullptr); ++ g_assert (state->terminal == terminal); ++ ++ return state->contents[state->contents_flip].caret; ++} ++ ++static gboolean ++vte_accessible_text_get_selection (GtkAccessibleText *accessible, ++ gsize *n_ranges, ++ GtkAccessibleTextRange **ranges) ++{ ++ VteTerminal *terminal = VTE_TERMINAL (accessible); ++ VteAccessibleText *state = vte_accessible_text_get (terminal); ++ ++ g_assert (VTE_IS_TERMINAL (terminal)); ++ g_assert (ranges != nullptr); ++ ++ *n_ranges = 0; ++ *ranges = nullptr; ++ ++ try { ++ auto impl = _vte_terminal_get_impl (terminal); ++ VteAccessibleTextContents *contents = &state->contents[state->contents_flip]; ++ GtkAccessibleTextRange range; ++ ++ if (impl->m_selection_resolved.empty() || ++ impl->m_selection[vte::to_integral(vte::platform::ClipboardType::PRIMARY)] == nullptr) ++ return FALSE; ++ ++ auto start_column = impl->m_selection_resolved.start_column(); ++ auto start_row = impl->m_selection_resolved.start_row(); ++ auto end_column = impl->m_selection_resolved.end_column(); ++ auto end_row = impl->m_selection_resolved.end_row(); ++ ++ auto start_offset = vte_accessible_text_contents_offset_from_xy (contents, start_column, start_row); ++ auto end_offset = vte_accessible_text_contents_offset_from_xy (contents, end_column, end_row); ++ ++ range.start = gsize(start_offset); ++ range.length = gsize(end_offset - start_offset); ++ ++ *n_ranges = 1; ++ *ranges = (GtkAccessibleTextRange *)g_memdup2 (&range, sizeof range); ++ ++ return TRUE; ++ } catch (...) { } ++ ++ return FALSE; ++} ++ ++static gboolean ++vte_accessible_text_get_attributes (GtkAccessibleText *accessible, ++ guint offset, ++ gsize *n_ranges, ++ GtkAccessibleTextRange **ranges, ++ char ***attribute_names, ++ char ***attribute_values) ++{ ++ VteTerminal *terminal = VTE_TERMINAL (accessible); ++ VteAccessibleText *state = vte_accessible_text_get (terminal); ++ VteAccessibleTextContents *contents; ++ struct _VteCharAttributes cur_attr; ++ struct _VteCharAttributes attr; ++ GtkAccessibleTextRange range; ++ struct { ++ const char *name; ++ const char *value; ++ } attrs[4]; ++ char fg_color[16]; ++ char bg_color[16]; ++ guint n_attrs = 0; ++ guint start = 0; ++ guint end = 0; ++ guint i; ++ ++ g_assert (VTE_IS_TERMINAL (accessible)); ++ g_assert (ranges != nullptr); ++ g_assert (attribute_names != nullptr); ++ g_assert (attribute_values != nullptr); ++ ++ contents = &state->contents[state->contents_flip]; ++ ++ *n_ranges = 0; ++ *ranges = nullptr; ++ *attribute_names = nullptr; ++ *attribute_values = nullptr; ++ ++ attr = *vte_char_attr_list_get (&contents->attrs, offset); ++ start = 0; ++ for (i = offset; i--;) { ++ cur_attr = *vte_char_attr_list_get (&contents->attrs, i); ++ if (!_pango_color_equal (&cur_attr.fore, &attr.fore) || ++ !_pango_color_equal (&cur_attr.back, &attr.back) || ++ cur_attr.underline != attr.underline || ++ cur_attr.strikethrough != attr.strikethrough) { ++ start = i + 1; ++ break; ++ } ++ } ++ end = vte_char_attr_list_get_size (&contents->attrs) - 1; ++ for (i = offset + 1; i < vte_char_attr_list_get_size (&contents->attrs); i++) { ++ cur_attr = *vte_char_attr_list_get (&contents->attrs, i); ++ if (!_pango_color_equal (&cur_attr.fore, &attr.fore) || ++ !_pango_color_equal (&cur_attr.back, &attr.back) || ++ cur_attr.underline != attr.underline || ++ cur_attr.strikethrough != attr.strikethrough) { ++ end = i - 1; ++ break; ++ } ++ } ++ ++ range.start = start; ++ range.length = end - start; ++ ++ if (range.length == 0) ++ return FALSE; ++ ++ if (attr.underline) { ++ attrs[n_attrs].name = "underline"; ++ attrs[n_attrs].value = "true"; ++ n_attrs++; ++ } ++ ++ if (attr.strikethrough) { ++ attrs[n_attrs].name = "strikethrough"; ++ attrs[n_attrs].value = "true"; ++ n_attrs++; ++ } ++ ++ g_snprintf (fg_color, sizeof fg_color, "%u,%u,%u", ++ attr.fore.red, attr.fore.green, attr.fore.blue); ++ attrs[n_attrs].name = "fg-color"; ++ attrs[n_attrs].value = fg_color; ++ n_attrs++; ++ ++ g_snprintf (bg_color, sizeof bg_color, "%u,%u,%u", ++ attr.back.red, attr.back.green, attr.back.blue); ++ attrs[n_attrs].name = "bg-color"; ++ attrs[n_attrs].value = bg_color; ++ n_attrs++; ++ ++ *attribute_names = g_new0 (char *, n_attrs + 1); ++ *attribute_values = g_new0 (char *, n_attrs + 1); ++ *n_ranges = 1; ++ *ranges = (GtkAccessibleTextRange *)g_memdup2 (&range, sizeof range); ++ ++ for (i = 0; i < n_attrs; i++) { ++ (*attribute_names)[i] = g_strdup (attrs[i].name); ++ (*attribute_values)[i] = g_strdup (attrs[i].value); ++ } ++ ++ return TRUE; ++} ++ ++void ++_vte_accessible_text_iface_init (GtkAccessibleTextInterface *iface) ++{ ++ iface->get_attributes = vte_accessible_text_get_attributes; ++ iface->get_caret_position = vte_accessible_text_get_caret_position; ++ iface->get_contents = vte_accessible_text_get_contents; ++ iface->get_contents_at = vte_accessible_text_get_contents_at; ++ iface->get_selection = vte_accessible_text_get_selection; ++} ++ ++static void ++vte_accessible_text_contents_changed (VteTerminal *terminal, ++ VteAccessibleText *state) ++{ ++ VteAccessibleTextContents *next = nullptr; ++ VteAccessibleTextContents *prev = nullptr; ++ const char *nextstr; ++ const char *prevstr; ++ gsize prevlen; ++ gsize nextlen; ++ ++ g_assert (VTE_IS_TERMINAL (terminal)); ++ g_assert (state != nullptr); ++ g_assert (state->terminal == terminal); ++ ++ if (!vte_terminal_get_enable_a11y (terminal)) ++ return; ++ ++ prev = &state->contents[state->contents_flip]; ++ next = &state->contents[!state->contents_flip]; ++ ++ /* Get a new snapshot of contents so that we can compare this to the ++ * previous contents. That way we can discover if it was a backspace ++ * that occurred or if it's more than that. ++ * ++ * We do not filp state->contents_flip immediately so that we can ++ * allow the AT context the ability to access the current contents ++ * on DELETE operations. ++ */ ++ vte_accessible_text_contents_reset (next); ++ vte_accessible_text_contents_snapshot (next, state->terminal); ++ ++ nextstr = vte_accessible_text_contents_get_string (next, &nextlen); ++ prevstr = vte_accessible_text_contents_get_string (prev, &prevlen); ++ ++ vte_assert_cmpint (char_positions_get_size (&prev->characters), ==, prev->n_chars); ++ vte_assert_cmpint (char_positions_get_size (&next->characters), ==, next->n_chars); ++ ++ /* NOTE: ++ * ++ * The code below is based upon what vteaccess.cc did for GTK 3. ++ * It does not do any sort of appropriate diffing to try to handle ++ * scrolling correctly. That would be a good idea to implement in ++ * the longer term. ++ * ++ * It just looks for a long prefix match, and then a long suffix ++ * match and attempts to diff what is between those to end points. ++ */ ++ ++ const char *prevc = prevstr; ++ const char *nextc = nextstr; ++ gsize offset = 0; ++ ++ /* Find the beginning of changes */ ++ while ((offset < prev->n_chars) && (offset < next->n_chars)) { ++ gunichar prevch = g_utf8_get_char (prevc); ++ gunichar nextch = g_utf8_get_char (nextc); ++ ++ if (prevch != nextch) { ++ break; ++ } ++ ++ offset++; ++ ++ prevc = g_utf8_next_char (prevc); ++ nextc = g_utf8_next_char (nextc); ++ } ++ ++ /* Find the end of changes */ ++ gsize next_end = next->n_chars; ++ gsize prev_end = prev->n_chars; ++ ++ prevc = prevstr + prevlen; ++ nextc = nextstr + nextlen; ++ ++ while ((next_end > offset) && (prev_end > offset)) { ++ prevc = g_utf8_prev_char (prevc); ++ nextc = g_utf8_prev_char (nextc); ++ ++ gunichar prevch = g_utf8_get_char (prevc); ++ gunichar nextch = g_utf8_get_char (nextc); ++ ++ if (prevch != nextch) { ++ break; ++ } ++ ++ next_end--; ++ prev_end--; ++ } ++ ++ if (offset < prev_end) { ++ gtk_accessible_text_update_contents (GTK_ACCESSIBLE_TEXT (terminal), ++ GTK_ACCESSIBLE_TEXT_CONTENT_CHANGE_REMOVE, ++ offset, prev_end); ++ } ++ ++ state->contents_flip = !state->contents_flip; ++ ++ if (offset < next_end) { ++ gtk_accessible_text_update_contents (GTK_ACCESSIBLE_TEXT (terminal), ++ GTK_ACCESSIBLE_TEXT_CONTENT_CHANGE_INSERT, ++ offset, next_end); ++ } ++ ++ if (prev->caret != next->caret) { ++ gtk_accessible_text_update_caret_position (GTK_ACCESSIBLE_TEXT (terminal)); ++ } ++} ++ ++static void ++vte_accessible_text_cursor_moved (VteTerminal *terminal, ++ VteAccessibleText *state) ++{ ++ VteAccessibleTextContents *contents = nullptr; ++ ++ g_assert (VTE_IS_TERMINAL (terminal)); ++ g_assert (state != nullptr); ++ g_assert (state->terminal == terminal); ++ ++ if (!vte_terminal_get_enable_a11y (terminal)) ++ return; ++ ++ contents = &state->contents[state->contents_flip]; ++ ++ long ccol, crow; ++ vte_terminal_get_cursor_position (terminal, &ccol, &crow); ++ if (ccol == contents->cached_caret_column && crow == contents->cached_caret_row) { ++ return; ++ } ++ ++ _vte_debug_print (VTE_DEBUG_ALLY, "Cursor at (%ld, " "%ld).\n", ccol, crow); ++ ++ contents->cached_caret_column = ccol; ++ contents->cached_caret_row = crow; ++ contents->caret = vte_accessible_text_contents_find_caret (contents, ccol, crow); ++ ++ gtk_accessible_text_update_caret_position (GTK_ACCESSIBLE_TEXT (terminal)); ++} ++ ++static void ++vte_accessible_text_window_title_changed (VteTerminal *terminal, ++ VteAccessibleText *state) ++{ ++ const char *window_title; ++ ++ g_assert (VTE_IS_TERMINAL (terminal)); ++ g_assert (state != nullptr); ++ g_assert (state->terminal == terminal); ++ ++ if (!vte_terminal_get_enable_a11y (terminal)) ++ return; ++ ++ window_title = vte_terminal_get_window_title (terminal); ++ ++ gtk_accessible_update_property (GTK_ACCESSIBLE (terminal), ++ GTK_ACCESSIBLE_PROPERTY_DESCRIPTION, window_title ? window_title : "", ++ GTK_ACCESSIBLE_VALUE_UNDEFINED); ++} ++ ++static void ++vte_accessible_text_selection_changed (VteTerminal *terminal, ++ VteAccessibleText *state) ++{ ++ g_assert (VTE_IS_TERMINAL (terminal)); ++ g_assert (state != nullptr); ++ g_assert (state->terminal == terminal); ++ ++ if (!vte_terminal_get_enable_a11y (terminal)) ++ return; ++ ++ gtk_accessible_text_update_caret_position (GTK_ACCESSIBLE_TEXT (terminal)); ++ gtk_accessible_text_update_selection_bound (GTK_ACCESSIBLE_TEXT (terminal)); ++} ++ ++void ++_vte_accessible_text_init (GtkAccessibleText *accessible) ++{ ++ VteTerminal *terminal = VTE_TERMINAL (accessible); ++ VteAccessibleText *state; ++ ++ state = g_new0 (VteAccessibleText, 1); ++ state->terminal = terminal; ++ ++ vte_accessible_text_contents_init (&state->contents[0]); ++ vte_accessible_text_contents_init (&state->contents[1]); ++ ++ g_object_set_data_full (G_OBJECT (terminal), ++ "VTE_ACCESSIBLE_TEXT", ++ state, ++ (GDestroyNotify)vte_accessible_text_free); ++ ++ g_signal_connect (terminal, ++ "contents-changed", ++ G_CALLBACK (vte_accessible_text_contents_changed), ++ state); ++ g_signal_connect (terminal, ++ "cursor-moved", ++ G_CALLBACK (vte_accessible_text_cursor_moved), ++ state); ++ g_signal_connect (terminal, ++ "selection-changed", ++ G_CALLBACK (vte_accessible_text_selection_changed), ++ state); ++ g_signal_connect (terminal, ++ "window-title-changed", ++ G_CALLBACK (vte_accessible_text_window_title_changed), ++ state); ++ ++ const char *window_title = vte_terminal_get_window_title (terminal); ++ ++ gtk_accessible_update_property (GTK_ACCESSIBLE (accessible), ++ GTK_ACCESSIBLE_PROPERTY_DESCRIPTION, window_title ? window_title : "", ++ GTK_ACCESSIBLE_PROPERTY_HAS_POPUP, TRUE, ++ GTK_ACCESSIBLE_PROPERTY_LABEL, "Terminal", ++ GTK_ACCESSIBLE_PROPERTY_MULTI_LINE, TRUE, ++ GTK_ACCESSIBLE_VALUE_UNDEFINED); ++} +diff --git a/src/vteaccess-gtk4.h b/src/vteaccess-gtk4.h +new file mode 100644 +index 00000000..37b09c7b +--- /dev/null ++++ b/src/vteaccess-gtk4.h +@@ -0,0 +1,25 @@ ++/* ++ * Copyright © 2024 Christian Hergert ++ * ++ * 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 . ++ */ ++ ++#include ++ ++G_BEGIN_DECLS ++ ++void _vte_accessible_text_iface_init (GtkAccessibleTextInterface *iface); ++void _vte_accessible_text_init (GtkAccessibleText *accessible); ++ ++G_END_DECLS +diff --git a/src/vteaccess.cc b/src/vteaccess.cc +index fff918dc..c1c1a4c0 100644 +--- a/src/vteaccess.cc ++++ b/src/vteaccess.cc +@@ -386,6 +386,12 @@ _vte_terminal_accessible_text_modified(VteTerminalAccessible* accessible) + glong offset, caret_offset, olen, clen; + gint old_snapshot_caret; + ++ auto widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(accessible)); ++ auto terminal = VTE_TERMINAL(widget); ++ ++ if (!vte_terminal_get_enable_a11y (terminal)) ++ return; ++ + old_snapshot_caret = priv->snapshot_caret; + priv->snapshot_contents_invalid = TRUE; + vte_terminal_accessible_update_private_data_if_needed(accessible, +@@ -541,6 +547,9 @@ _vte_terminal_accessible_text_scrolled(VteTerminalAccessible* accessible, + auto widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(accessible)); + auto terminal = VTE_TERMINAL(widget); + ++ if (!vte_terminal_get_enable_a11y (terminal)) ++ return; ++ + row_count = vte_terminal_get_row_count(terminal); + if (((howmuch < 0) && (howmuch <= -row_count)) || + ((howmuch > 0) && (howmuch >= row_count))) { +@@ -793,6 +802,9 @@ vte_terminal_accessible_invalidate_cursor(VteTerminal *terminal, gpointer data) + VteTerminalAccessible *accessible = (VteTerminalAccessible *)data; + VteTerminalAccessiblePrivate *priv = (VteTerminalAccessiblePrivate *)_vte_terminal_accessible_get_instance_private(accessible); + ++ if (!vte_terminal_get_enable_a11y (terminal)) ++ return; ++ + _vte_debug_print(VTE_DEBUG_ALLY, + "Invalidating accessibility cursor.\n"); + priv->snapshot_caret_invalid = TRUE; +@@ -807,6 +819,9 @@ vte_terminal_accessible_title_changed(VteTerminal *terminal, gpointer data) + { + VteTerminalAccessible *accessible = (VteTerminalAccessible *)data; + ++ if (!vte_terminal_get_enable_a11y (terminal)) ++ return; ++ + atk_object_set_description(ATK_OBJECT(accessible), vte_terminal_get_window_title(terminal)); + } + +@@ -820,6 +835,9 @@ vte_terminal_accessible_visibility_notify(VteTerminal *terminal, + GtkWidget *widget; + gboolean visible; + ++ if (!vte_terminal_get_enable_a11y (terminal)) ++ return FALSE; ++ + visible = event->state != GDK_VISIBILITY_FULLY_OBSCURED; + /* The VISIBLE state indicates that this widget is "visible". */ + atk_object_notify_state_change(ATK_OBJECT(accessible), +@@ -851,6 +869,9 @@ vte_terminal_accessible_selection_changed (VteTerminal *terminal, + { + VteTerminalAccessible *accessible = (VteTerminalAccessible *)data; + ++ if (!vte_terminal_get_enable_a11y (terminal)) ++ return; ++ + g_signal_emit_by_name (accessible, "text_selection_changed"); + } + +diff --git a/src/vtegtk.cc b/src/vtegtk.cc +index c713a95a..6ece18e9 100644 +--- a/src/vtegtk.cc ++++ b/src/vtegtk.cc +@@ -65,9 +65,11 @@ + #include + + #if WITH_A11Y +-#if VTE_GTK == 3 +-#include "vteaccess.h" +-#endif /* VTE_GTK == 3 */ ++# if VTE_GTK == 3 ++# include "vteaccess.h" ++# elif VTE_GTK == 4 ++# include "vteaccess-gtk4.h" ++# endif + #endif /* WITH_A11Y */ + + #if WITH_ICU +@@ -155,6 +157,14 @@ private: + std::shared_ptr m_widget; + }; + ++#if defined(WITH_A11Y) && VTE_GTK == 4 ++# define VTE_IMPLEMENT_ACCESSIBLE \ ++ G_IMPLEMENT_INTERFACE(GTK_TYPE_ACCESSIBLE_TEXT, \ ++ _vte_accessible_text_iface_init) ++#else ++# define VTE_IMPLEMENT_ACCESSIBLE ++#endif ++ + #if VTE_DEBUG + G_DEFINE_TYPE_WITH_CODE(VteTerminal, vte_terminal, GTK_TYPE_WIDGET, + { +@@ -163,6 +173,7 @@ G_DEFINE_TYPE_WITH_CODE(VteTerminal, vte_terminal, GTK_TYPE_WIDGET, + } + g_type_add_class_private (g_define_type_id, sizeof (VteTerminalClassPrivate)); + G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE, nullptr) ++ VTE_IMPLEMENT_ACCESSIBLE + if (_vte_debug_on(VTE_DEBUG_LIFECYCLE)) { + g_printerr("vte_terminal_get_type()\n"); + }) +@@ -173,7 +184,8 @@ G_DEFINE_TYPE_WITH_CODE(VteTerminal, vte_terminal, GTK_TYPE_WIDGET, + g_type_add_instance_private(g_define_type_id, sizeof(VteTerminalPrivate)); + } + g_type_add_class_private (g_define_type_id, sizeof (VteTerminalClassPrivate)); +- G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE, nullptr)) ++ G_IMPLEMENT_INTERFACE(GTK_TYPE_SCROLLABLE, nullptr) ++ VTE_IMPLEMENT_ACCESSIBLE) + #endif + + static inline auto +@@ -901,6 +913,10 @@ try + gtk_widget_set_has_window(&terminal->widget, FALSE); + #endif + ++#if defined(WITH_A11Y) && VTE_GTK == 4 ++ _vte_accessible_text_init (GTK_ACCESSIBLE_TEXT (terminal)); ++#endif ++ + place = vte_terminal_get_instance_private(terminal); + new (place) VteTerminalPrivate{terminal}; + } +@@ -1017,6 +1033,9 @@ try + case PROP_DELETE_BINDING: + g_value_set_enum (value, widget->delete_binding()); + break; ++ case PROP_ENABLE_A11Y: ++ g_value_set_boolean (value, vte_terminal_get_enable_a11y (terminal)); ++ break; + case PROP_ENABLE_BIDI: + g_value_set_boolean (value, vte_terminal_get_enable_bidi (terminal)); + break; +@@ -1172,6 +1191,9 @@ try + case PROP_DELETE_BINDING: + vte_terminal_set_delete_binding (terminal, (VteEraseBinding)g_value_get_enum (value)); + break; ++ case PROP_ENABLE_A11Y: ++ vte_terminal_set_enable_a11y (terminal, g_value_get_boolean (value)); ++ break; + case PROP_ENABLE_BIDI: + vte_terminal_set_enable_bidi (terminal, g_value_get_boolean (value)); + break; +@@ -2286,6 +2308,22 @@ vte_terminal_class_init(VteTerminalClass *klass) + VTE_ERASE_AUTO, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); + ++ /** ++ * VteTerminal:enable-a11y: ++ * ++ * Controls whether or not a11y is enabled for the widget. ++ * ++ * Since: 0.78 ++ */ ++ pspecs[PROP_ENABLE_A11Y] = ++ g_param_spec_boolean ("enable-a11y", NULL, NULL, ++#if VTE_GTK == 3 ++ TRUE, ++#else ++ FALSE, ++#endif ++ (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); ++ + /** + * VteTerminal:enable-bidi: + * +@@ -2712,10 +2750,12 @@ vte_terminal_class_init(VteTerminalClass *klass) + err.assert_no_error(); + #endif + +-#if VTE_GTK == 3 + #if WITH_A11Y ++#if VTE_GTK == 3 + /* a11y */ + gtk_widget_class_set_accessible_type(widget_class, VTE_TYPE_TERMINAL_ACCESSIBLE); ++#elif VTE_GTK == 4 ++ gtk_widget_class_set_accessible_role(widget_class, GTK_ACCESSIBLE_ROLE_TERMINAL); + #endif + #endif + } +@@ -5709,6 +5749,53 @@ catch (...) + vte::log_exception(); + } + ++/** ++ * vte_terminal_get_enable_a11y: ++ * @terminal: a #VteTerminal ++ * ++ * Checks whether the terminal communicates with a11y backends ++ * ++ * Returns: %TRUE if a11y is enabled, %FALSE if not ++ * ++ * Since: 0.78 ++ */ ++gboolean ++vte_terminal_get_enable_a11y(VteTerminal *terminal) noexcept ++try ++{ ++ g_return_val_if_fail(VTE_IS_TERMINAL(terminal), false); ++ return IMPL(terminal)->m_enable_a11y; ++} ++catch (...) ++{ ++ vte::log_exception(); ++ return false; ++} ++ ++/** ++ * vte_terminal_set_enable_a11y: ++ * @terminal: a #VteTerminal ++ * @enable_a11y: %TRUE to enable a11y support ++ * ++ * Controls whether or not the terminal will communicate with a11y backends. ++ * ++ * Since: 0.78 ++ */ ++void ++vte_terminal_set_enable_a11y(VteTerminal *terminal, ++ gboolean enable_a11y) noexcept ++try ++{ ++ g_return_if_fail(VTE_IS_TERMINAL(terminal)); ++ ++ if (IMPL(terminal)->set_enable_a11y(enable_a11y != FALSE)) ++ g_object_notify_by_pspec(G_OBJECT(terminal), pspecs[PROP_ENABLE_A11Y]); ++} ++catch (...) ++{ ++ vte::log_exception(); ++} ++ + /** + * vte_terminal_get_enable_bidi: + * @terminal: a #VteTerminal +diff --git a/src/vtegtk.hh b/src/vtegtk.hh +index 566c8508..87259680 100644 +--- a/src/vtegtk.hh ++++ b/src/vtegtk.hh +@@ -80,6 +80,7 @@ enum { + PROP_CURRENT_DIRECTORY_URI, + PROP_CURRENT_FILE_URI, + PROP_DELETE_BINDING, ++ PROP_ENABLE_A11Y, + PROP_ENABLE_BIDI, + PROP_ENABLE_FALLBACK_SCROLLING, + PROP_ENABLE_SHAPING, +diff --git a/src/vteinternal.hh b/src/vteinternal.hh +index 07a9e993..6e2c2e7f 100644 +--- a/src/vteinternal.hh ++++ b/src/vteinternal.hh +@@ -799,6 +799,13 @@ public: + const char *m_hyperlink_hover_uri; /* data is owned by the ring */ + long m_hyperlink_auto_id{0}; + ++ /* Accessibility support */ ++#if VTE_GTK == 3 ++ bool m_enable_a11y{true}; ++#elif VTE_GTK == 4 ++ bool m_enable_a11y{false}; ++#endif ++ + /* RingView and friends */ + vte::base::RingView m_ringview; + bool m_enable_bidi{true}; +@@ -1528,6 +1535,7 @@ public: + bool set_cursor_style(CursorStyle style); + bool set_delete_binding(EraseMode binding); + auto delete_binding() const noexcept { return m_delete_binding; } ++ bool set_enable_a11y(bool setting); + bool set_enable_bidi(bool setting); + bool set_enable_shaping(bool setting); + bool set_encoding(char const* codeset, +-- +2.43.1 + diff --git a/SOURCES/0001-add-notification-and-shell-precmd-preexec.patch b/SOURCES/0001-add-notification-and-shell-precmd-preexec.patch new file mode 100644 index 0000000..ffc02b2 --- /dev/null +++ b/SOURCES/0001-add-notification-and-shell-precmd-preexec.patch @@ -0,0 +1,533 @@ +From f31af265a19a406cd193a82b96dff1dd2e4595b4 Mon Sep 17 00:00:00 2001 +From: Christian Hergert +Date: Mon, 4 Mar 2024 14:03:38 -0800 +Subject: [PATCH] add notification and shell precmd/preexec + +This is a bit simpler to manage as a single patch rather than a series of +patches which incrementally update things. + +This alters some of the original patches so that we don't need to have +such careful integration with the vtable of the class as that isn't used. + +You can always connect to the signal rather than the vtable default func. +--- + src/marshal.list | 1 + + src/vte.cc | 28 +++++++++ + src/vte.sh.in | 7 ++- + src/vte/vteterminal.h | 4 ++ + src/vtegtk.cc | 131 ++++++++++++++++++++++++++++++++++++++++++ + src/vtegtk.hh | 5 ++ + src/vteinternal.hh | 26 +++++++++ + src/vteseq.cc | 123 ++++++++++++++++++++++++++++++++++++++- + 8 files changed, 322 insertions(+), 3 deletions(-) + +diff --git a/src/marshal.list b/src/marshal.list +index 241128c3..f9b3818f 100644 +--- a/src/marshal.list ++++ b/src/marshal.list +@@ -1,3 +1,4 @@ + VOID:STRING,BOXED + VOID:STRING,UINT ++VOID:STRING,STRING + VOID:UINT,UINT +diff --git a/src/vte.cc b/src/vte.cc +index 2cba7369..a8a0e22c 100644 +--- a/src/vte.cc ++++ b/src/vte.cc +@@ -10771,6 +10771,34 @@ Terminal::emit_pending_signals() + + emit_adjustment_changed(); + ++ if (m_pending_changes & vte::to_integral(PendingChanges::NOTIFICATION)) { ++ _vte_debug_print (VTE_DEBUG_SIGNALS, ++ "Emitting `notification-received'.\n"); ++ g_signal_emit(freezer.get(), signals[SIGNAL_NOTIFICATION_RECEIVED], 0, ++ m_notification_summary.c_str(), ++ m_notification_body.c_str()); ++ } ++ ++ if (m_pending_changes & vte::to_integral(PendingChanges::SHELL_PREEXEC)) { ++ _vte_debug_print (VTE_DEBUG_SIGNALS, ++ "Emitting `shell-preexec'.\n"); ++ g_signal_emit(freezer.get(), signals[SIGNAL_SHELL_PREEXEC], 0); ++ } ++ ++ if (m_pending_changes & vte::to_integral(PendingChanges::SHELL_PRECMD)) { ++ _vte_debug_print (VTE_DEBUG_SIGNALS, ++ "Emitting `shell-precmd'.\n"); ++ g_signal_emit(freezer.get(), signals[SIGNAL_SHELL_PRECMD], 0); ++ } ++ ++ if (m_pending_changes & vte::to_integral(PendingChanges::CONTAINERS)) { ++ _vte_debug_print(VTE_DEBUG_SIGNALS, ++ "Notifying `current-container-name' and `current-container-runtime'.\n"); ++ ++ g_object_notify_by_pspec(freezer.get(), pspecs[PROP_CURRENT_CONTAINER_NAME]); ++ g_object_notify_by_pspec(freezer.get(), pspecs[PROP_CURRENT_CONTAINER_RUNTIME]); ++ } ++ + if (m_pending_changes & vte::to_integral(PendingChanges::TITLE)) { + if (m_window_title != m_window_title_pending) { + m_window_title.swap(m_window_title_pending); +diff --git a/src/vte.sh.in b/src/vte.sh.in +index 2328a9ec..93f45ea8 100644 +--- a/src/vte.sh.in ++++ b/src/vte.sh.in +@@ -28,6 +28,12 @@ case "$TERM" in + *) return 0 ;; + esac + ++__vte_shell_precmd() { ++ local command=$(HISTTIMEFORMAT= history 1 | sed 's/^ *[0-9]\+ *//') ++ command="${command//;/ }" ++ printf '\033]777;notify;Command completed;%s\033\\\033]777;precmd\033\\' "${command}" ++} ++ + __vte_osc7 () { + printf "\033]7;file://%s%s\033\\" "${HOSTNAME}" "$(@libexecdir@/vte-urlencode-cwd)" + } +@@ -37,6 +43,7 @@ __vte_prompt_command() { + [ "$PWD" != "$HOME" ] && pwd=${PWD/#$HOME\//\~\/} + pwd="${pwd//[[:cntrl:]]}" + printf "\033]0;%s@%s:%s\033\\" "${USER}" "${HOSTNAME%%.*}" "${pwd}" ++ __vte_shell_precmd + __vte_osc7 + } + +@@ -49,9 +56,12 @@ if [[ -n "${BASH_VERSION:-}" ]]; then + # use the __vte_prompt_command function which also sets the title. + + if [[ "$(declare -p PROMPT_COMMAND 2>&1)" =~ "declare -a" ]]; then ++ PROMPT_COMMAND+=(__vte_shell_precmd) + PROMPT_COMMAND+=(__vte_osc7) ++ PS0=$(printf "\033]777;preexec\033\\") + else + PROMPT_COMMAND="__vte_prompt_command" ++ PS0=$(printf "\033]777;preexec\033\\") + fi + + # Shell integration +diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h +index a9e1e494..9c2e2dae 100644 +--- a/src/vte/vteterminal.h ++++ b/src/vte/vteterminal.h +@@ -559,6 +559,10 @@ glong vte_terminal_get_column_count(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VT + _VTE_PUBLIC + const char *vte_terminal_get_window_title(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); + _VTE_PUBLIC ++const char *vte_terminal_get_current_container_name(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); ++_VTE_PUBLIC ++const char *vte_terminal_get_current_container_runtime(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); ++_VTE_PUBLIC + const char *vte_terminal_get_current_directory_uri(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); + _VTE_PUBLIC + const char *vte_terminal_get_current_file_uri(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); +diff --git a/src/vtegtk.cc b/src/vtegtk.cc +index 92eb6881..c713a95a 100644 +--- a/src/vtegtk.cc ++++ b/src/vtegtk.cc +@@ -999,6 +999,12 @@ try + case PROP_CURSOR_BLINK_MODE: + g_value_set_enum (value, vte_terminal_get_cursor_blink_mode (terminal)); + break; ++ case PROP_CURRENT_CONTAINER_NAME: ++ g_value_set_string (value, vte_terminal_get_current_container_name (terminal)); ++ break; ++ case PROP_CURRENT_CONTAINER_RUNTIME: ++ g_value_set_string (value, vte_terminal_get_current_container_runtime (terminal)); ++ break; + case PROP_CURRENT_DIRECTORY_URI: + g_value_set_string (value, vte_terminal_get_current_directory_uri (terminal)); + break; +@@ -1434,6 +1440,60 @@ vte_terminal_class_init(VteTerminalClass *klass) + G_OBJECT_CLASS_TYPE(klass), + g_cclosure_marshal_VOID__INTv); + ++ /** ++ * VteTerminal::notification-received: ++ * @vteterminal: the object which received the signal ++ * @summary: The summary ++ * @body: (allow-none): Extra optional text ++ * ++ * Emitted when a process running in the terminal wants to ++ * send a notification to the desktop environment. ++ */ ++ signals[SIGNAL_NOTIFICATION_RECEIVED] = ++ g_signal_new(I_("notification-received"), ++ G_OBJECT_CLASS_TYPE(klass), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, ++ NULL, ++ _vte_marshal_VOID__STRING_STRING, ++ G_TYPE_NONE, ++ 2, G_TYPE_STRING, G_TYPE_STRING); ++ ++ /** ++ * VteTerminal::shell-precmd: ++ * @vteterminal: the object which received the signal ++ * ++ * Emitted right before an interactive shell shows a ++ * first-level prompt. ++ */ ++ signals[SIGNAL_SHELL_PRECMD] = ++ g_signal_new(I_("shell-precmd"), ++ G_OBJECT_CLASS_TYPE(klass), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, ++ NULL, ++ g_cclosure_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ ++ /** ++ * VteTerminal::shell-preexec: ++ * @vteterminal: the object which received the signal ++ * ++ * Emitted when the interactive shell has read in a complete ++ * command and is about to execute it. ++ */ ++ signals[SIGNAL_SHELL_PREEXEC] = ++ g_signal_new(I_("shell-preexec"), ++ G_OBJECT_CLASS_TYPE(klass), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, ++ NULL, ++ g_cclosure_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ + /** + * VteTerminal::window-title-changed: + * @vteterminal: the object which received the signal +@@ -2487,6 +2547,27 @@ vte_terminal_class_init(VteTerminalClass *klass) + NULL, + (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); + ++ /** ++ * VteTerminal:current-container-name: ++ * ++ * The name of the current container, or %NULL if unset. ++ */ ++ pspecs[PROP_CURRENT_CONTAINER_NAME] = ++ g_param_spec_string ("current-container-name", NULL, NULL, ++ NULL, ++ (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); ++ ++ /** ++ * VteTerminal:current-container-runtime: ++ * ++ * The name of the runtime toolset used to set up the current ++ * container, or %NULL if unset. ++ */ ++ pspecs[PROP_CURRENT_CONTAINER_RUNTIME] = ++ g_param_spec_string ("current-container-runtime", NULL, NULL, ++ NULL, ++ (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); ++ + /** + * VteTerminal:current-directory-uri: + * +@@ -5419,6 +5500,56 @@ catch (...) + return -1; + } + ++/** ++ * vte_terminal_get_current_container_name: ++ * @terminal: a #VteTerminal ++ * ++ * Returns: (nullable) (transfer none): the name of the current ++ * container, or %NULL ++ */ ++const char * ++vte_terminal_get_current_container_name(VteTerminal *terminal) noexcept ++try ++{ ++ g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL); ++ auto impl = IMPL(terminal); ++ if (impl->m_containers.empty()) ++ return NULL; ++ ++ const VteContainer &container = impl->m_containers.top(); ++ return container.m_name.c_str(); ++} ++catch (...) ++{ ++ vte::log_exception(); ++ return NULL; ++} ++ ++/** ++ * vte_terminal_get_current_container_runtime: ++ * @terminal: a #VteTerminal ++ * ++ * Returns: (nullable) (transfer none): the name of the runtime ++ * toolset used to set up the current container, or %NULL ++ */ ++const char * ++vte_terminal_get_current_container_runtime(VteTerminal *terminal) noexcept ++try ++{ ++ g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL); ++ auto impl = IMPL(terminal); ++ if (impl->m_containers.empty()) ++ return NULL; ++ ++ const VteContainer &container = impl->m_containers.top(); ++ return container.m_runtime.c_str(); ++} ++catch (...) ++{ ++ vte::log_exception(); ++ return NULL; ++} ++ + /** + * vte_terminal_get_current_directory_uri: + * @terminal: a #VteTerminal +diff --git a/src/vtegtk.hh b/src/vtegtk.hh +index 1d1383af..566c8508 100644 +--- a/src/vtegtk.hh ++++ b/src/vtegtk.hh +@@ -53,6 +53,9 @@ enum { + SIGNAL_RESTORE_WINDOW, + SIGNAL_SELECTION_CHANGED, + SIGNAL_SETUP_CONTEXT_MENU, ++ SIGNAL_SHELL_PRECMD, ++ SIGNAL_SHELL_PREEXEC, ++ SIGNAL_NOTIFICATION_RECEIVED, + SIGNAL_WINDOW_TITLE_CHANGED, + LAST_SIGNAL + }; +@@ -72,6 +75,8 @@ enum { + PROP_CONTEXT_MENU, + PROP_CURSOR_BLINK_MODE, + PROP_CURSOR_SHAPE, ++ PROP_CURRENT_CONTAINER_NAME, ++ PROP_CURRENT_CONTAINER_RUNTIME, + PROP_CURRENT_DIRECTORY_URI, + PROP_CURRENT_FILE_URI, + PROP_DELETE_BINDING, +diff --git a/src/vteinternal.hh b/src/vteinternal.hh +index ed57ad16..07a9e993 100644 +--- a/src/vteinternal.hh ++++ b/src/vteinternal.hh +@@ -63,6 +63,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -121,6 +122,18 @@ typedef enum _VteCharacterReplacement { + VTE_CHARACTER_REPLACEMENT_LINE_DRAWING + } VteCharacterReplacement; + ++struct VteContainer { ++public: ++ VteContainer(const std::string &name, const std::string &runtime) : ++ m_name{name}, ++ m_runtime{runtime} ++ { ++ } ++ ++ std::string m_name; ++ std::string m_runtime; ++}; ++ + typedef struct _VtePaletteColor { + struct { + vte::color::rgb color; +@@ -710,6 +723,12 @@ public: + gboolean m_cursor_moved_pending; + gboolean m_contents_changed_pending; + ++ /* desktop notification */ ++ std::stack m_containers; ++ ++ std::string m_notification_summary; ++ std::string m_notification_body; ++ + std::string m_window_title{}; + std::string m_current_directory_uri{}; + std::string m_current_file_uri{}; +@@ -723,6 +742,10 @@ public: + TITLE = 1u << 0, + CWD = 1u << 1, + CWF = 1u << 2, ++ NOTIFICATION = 1u << 4, ++ SHELL_PREEXEC = 1u << 5, ++ SHELL_PRECMD = 1u << 6, ++ CONTAINERS = 1u << 7, + }; + unsigned m_pending_changes{0}; + +@@ -1654,6 +1677,9 @@ public: + int osc) noexcept; + + /* OSC handlers */ ++ void handle_urxvt_extension(vte::parser::Sequence const& seq, ++ vte::parser::StringTokeniser::const_iterator& token, ++ vte::parser::StringTokeniser::const_iterator const& endtoken) noexcept; + void set_color(vte::parser::Sequence const& seq, + vte::parser::StringTokeniser::const_iterator& token, + vte::parser::StringTokeniser::const_iterator const& endtoken, +diff --git a/src/vteseq.cc b/src/vteseq.cc +index 904837e1..26f7b0d6 100644 +--- a/src/vteseq.cc ++++ b/src/vteseq.cc +@@ -39,6 +39,9 @@ + #define ST_C0 _VTE_CAP_ST + + #include ++#include ++#include ++#include + + using namespace std::literals; + +@@ -1276,6 +1279,121 @@ Terminal::erase_in_line(vte::parser::Sequence const& seq) + m_text_deleted_flag = TRUE; + } + ++void ++Terminal::handle_urxvt_extension(vte::parser::Sequence const& seq, ++ vte::parser::StringTokeniser::const_iterator& token, ++ vte::parser::StringTokeniser::const_iterator const& endtoken) noexcept ++{ ++ if (token == endtoken) ++ return; ++ ++ if (*token == "container") { ++ ++token; ++ ++ if (token == endtoken) ++ return; ++ ++ const std::string sub_command = *token; ++ ++token; ++ ++ if (sub_command == "pop") { ++ if (token == endtoken) ++ return; ++ ++ ++token; ++ ++ if (token == endtoken) ++ return; ++ ++ ++token; ++ ++ if (token == endtoken) { ++ if (!m_containers.empty()) { ++ m_containers.pop(); ++ m_pending_changes |= vte::to_integral(PendingChanges::CONTAINERS); ++ } ++ ++ return; ++ } ++ ++ const std::string uid_token = *token; ++ ++token; ++ ++ const uid_t uid = getuid(); ++ const std::string uid_str = std::to_string(uid); ++ ++ if (uid_token == uid_str) { ++ if (!m_containers.empty()) { ++ m_containers.pop(); ++ m_pending_changes |= vte::to_integral(PendingChanges::CONTAINERS); ++ } ++ ++ return; ++ } ++ ++ return; ++ } else if (sub_command == "push") { ++ if (token == endtoken) ++ return; ++ ++ const std::string name = *token; ++ ++token; ++ ++ if (token == endtoken) ++ return; ++ ++ const std::string runtime = *token; ++ ++token; ++ ++ if (token == endtoken) { ++ m_containers.emplace(name, runtime); ++ m_pending_changes |= vte::to_integral(PendingChanges::CONTAINERS); ++ return; ++ } ++ ++ const std::string uid_token = *token; ++ ++token; ++ ++ const uid_t uid = getuid(); ++ const std::string uid_str = std::to_string(uid); ++ ++ if (uid_token == uid_str) { ++ m_containers.emplace(name, runtime); ++ m_pending_changes |= vte::to_integral(PendingChanges::CONTAINERS); ++ return; ++ } ++ ++ return; ++ } ++ ++ return; ++ } ++ ++ if (*token == "notify") { ++ ++token; ++ ++ if (token == endtoken) ++ return; ++ ++ m_notification_summary = *token; ++ m_notification_body.clear(); ++ m_pending_changes |= vte::to_integral(PendingChanges::NOTIFICATION); ++ ++token; ++ ++ if (token == endtoken) ++ return; ++ ++ m_notification_body = *token; ++ return; ++ } ++ ++ if (*token == "precmd") { ++ m_pending_changes |= vte::to_integral(PendingChanges::SHELL_PRECMD); ++ } else if (*token == "preexec") { ++ m_pending_changes |= vte::to_integral(PendingChanges::SHELL_PREEXEC); ++ } ++} ++ + bool + Terminal::get_osc_color_index(int osc, + int value, +@@ -6596,6 +6714,10 @@ Terminal::OSC(vte::parser::Sequence const& seq) + reset_color(VTE_HIGHLIGHT_FG, VTE_COLOR_SOURCE_ESCAPE); + break; + ++ case VTE_OSC_URXVT_EXTENSION: ++ handle_urxvt_extension(seq, it, cend); ++ break; ++ + case VTE_OSC_XTERM_SET_ICON_TITLE: + case VTE_OSC_XTERM_SET_XPROPERTY: + case VTE_OSC_XTERM_SET_COLOR_MOUSE_CURSOR_FG: +@@ -6636,7 +6758,6 @@ Terminal::OSC(vte::parser::Sequence const& seq) + case VTE_OSC_URXVT_SET_FONT_BOLD_ITALIC: + case VTE_OSC_URXVT_VIEW_UP: + case VTE_OSC_URXVT_VIEW_DOWN: +- case VTE_OSC_URXVT_EXTENSION: + case VTE_OSC_YF_RQGWR: + default: + break; +-- +2.43.1 + diff --git a/SPECS/vte291.spec b/SPECS/vte291.spec new file mode 100644 index 0000000..594450d --- /dev/null +++ b/SPECS/vte291.spec @@ -0,0 +1,681 @@ +%global apiver 2.91 + +%global fribidi_version 1.0.0 +%global glib2_version 2.52.0 +%global gnutls_version 3.2.7 +%global gtk3_version 3.24.22 +%global gtk4_version 4.14.0 +%global icu_uc_version 4.8 +%global libsystemd_version 220 +%global pango_version 1.22.0 +%global pcre2_version 10.21 + +%ifarch x86_64 +%global use_b_symbolic true +%else +%global use_b_symbolic false +%endif + +Name: vte291 +Version: 0.76.3 +Release: 2%{?dist} +Summary: GTK+ 3 terminal emulator library + +# libvte-2.91.so is generated from LGPLv2+ and MIT sources +License: GPL-3.0-or-later AND LGPL-3.0-or-later AND MIT AND X11 AND CC-BY-4.0 + +URL: https://wiki.gnome.org/Apps/Terminal/VTE +Source0: https://download.gnome.org/sources/vte/0.76/vte-%{version}.tar.xz + +# https://bugzilla.gnome.org/show_bug.cgi?id=711059 +# https://bugzilla.redhat.com/show_bug.cgi?id=1103380 +# https://pagure.io/fedora-workstation/issue/216 +Patch: 0001-a11y-implement-GtkAccessibleText.patch +Patch: 0001-add-notification-and-shell-precmd-preexec.patch + +BuildRequires: pkgconfig(fribidi) >= %{fribidi_version} +BuildRequires: pkgconfig(gio-2.0) >= %{glib2_version} +BuildRequires: pkgconfig(glib-2.0) >= %{glib2_version} +BuildRequires: pkgconfig(gnutls) >= %{gnutls_version} +BuildRequires: pkgconfig(gobject-2.0) >= %{glib2_version} +BuildRequires: pkgconfig(gtk+-3.0) >= %{gtk3_version} +BuildRequires: pkgconfig(gtk4) >= %{gtk4_version} +BuildRequires: pkgconfig(icu-uc) >= %{icu_uc_version} +BuildRequires: pkgconfig(liblz4) +BuildRequires: pkgconfig(libpcre2-8) >= %{pcre2_version} +BuildRequires: pkgconfig(libsystemd) >= %{libsystemd_version} +BuildRequires: pkgconfig(pango) >= %{pango_version} +BuildRequires: gcc-c++ +BuildRequires: gettext +BuildRequires: gi-docgen +BuildRequires: gobject-introspection-devel +BuildRequires: gperf +BuildRequires: meson +BuildRequires: systemd-rpm-macros +BuildRequires: vala + +Requires: fribidi >= %{fribidi_version} +Requires: glib2 >= %{glib2_version} +Requires: gnutls%{?_isa} >= %{gnutls_version} +Requires: gtk3%{?_isa} >= %{gtk3_version} +Requires: libicu%{?_isa} >= %{icu_uc_version} +Requires: pango >= %{pango_version} +Requires: pcre2%{?_isa} >= %{pcre2_version} +Requires: systemd-libs%{?_isa} >= %{libsystemd_version} +Requires: vte-profile + +Conflicts: gnome-terminal < 3.20.1-2 + +%description +VTE is a library implementing a terminal emulator widget for GTK+. VTE +is mainly used in gnome-terminal, but can also be used to embed a +console/terminal in games, editors, IDEs, etc. + +%package gtk4 +Summary: GTK4 terminal emulator library + +# libvte-2.91.so is generated from LGPLv2+ and MIT sources +License: LGPL-3.0-or-later AND MIT AND X11 + +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description gtk4 +VTE is a library implementing a terminal emulator widget for GTK 4. VTE +is mainly used in gnome-terminal, but can also be used to embed a +console/terminal in games, editors, IDEs, etc. + +%package devel +Summary: Development files for GTK+ 3 %{name} + +# vte-2.91 is generated from GPLv3+ sources, while the public headers are +# LGPLv3+ +License: GPL-3.0-or-later AND LGPL-3.0-or-later + +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description devel +The %{name}-devel package contains libraries and header files for +developing GTK+ 3 applications that use %{name}. + +%package gtk4-devel +Summary: Development files for GTK 4 %{name} + +# vte-2.91 is generated from GPLv3+ sources, while the public headers are +# LGPLv3+ +License: GPL-3.0-or-later AND LGPL-3.0-or-later + +Requires: %{name}-gtk4%{?_isa} = %{version}-%{release} +Requires: %{name}-devel%{?_isa} = %{version}-%{release} + +%description gtk4-devel +The %{name}-gtk4-devel package contains libraries and header files for +developing GTK 4 applications that use %{name}. + +# vte-profile is deliberately not noarch to avoid having to obsolete a noarch +# subpackage in the future when we get rid of the vte3 / vte291 split. Yum is +# notoriously bad when handling noarch obsoletes and insists on installing both +# of the multilib packages (i686 + x86_64) as the replacement. +%package -n vte-profile +Summary: Profile script for VTE terminal emulator library +License: GPL-3.0-or-later +# vte.sh was previously part of the vte3 package +Conflicts: vte3 < 0.36.1-3 + +%description -n vte-profile +The vte-profile package contains a profile.d script for the VTE terminal +emulator library. + +%prep +%autosetup -p1 -n vte-%{version} +%if 0%{?flatpak} +# Install user units where systemd macros expect them +sed -i -e "/^vte_systemduserunitdir =/s|vte_prefix|'/usr'|" meson.build +%endif + +%build +%meson --buildtype=plain -Ddocs=true -Dgtk3=true -Dgtk4=true -D_b_symbolic_functions=%{use_b_symbolic} +%meson_build + +%install +%meson_install + +%find_lang vte-%{apiver} + +%files -f vte-%{apiver}.lang +%license COPYING.LGPL3 +%license COPYING.XTERM +%doc README.md +%{_libdir}/libvte-%{apiver}.so.0* +%dir %{_libdir}/girepository-1.0 +%{_libdir}/girepository-1.0/Vte-2.91.typelib +%{_userunitdir}/vte-spawn-.scope.d + +%files gtk4 +%{_libdir}/libvte-%{apiver}-gtk4.so.0* +%{_libdir}/girepository-1.0/Vte-3.91.typelib + +%files devel +%license COPYING.GPL3 +%{_bindir}/vte-%{apiver} +%{_includedir}/vte-%{apiver}/ +%{_libdir}/libvte-%{apiver}.so +%{_libdir}/pkgconfig/vte-%{apiver}.pc +%dir %{_datadir}/gir-1.0 +%{_datadir}/gir-1.0/Vte-2.91.gir +%{_datadir}/glade/ +%doc %{_docdir}/vte-2.91/ +%dir %{_datadir}/vala +%dir %{_datadir}/vala/vapi +%{_datadir}/vala/vapi/vte-2.91.deps +%{_datadir}/vala/vapi/vte-2.91.vapi + +%files gtk4-devel +%{_bindir}/vte-%{apiver}-gtk4 +%{_includedir}/vte-%{apiver}-gtk4/ +%{_libdir}/libvte-%{apiver}-gtk4.so +%{_libdir}/pkgconfig/vte-%{apiver}-gtk4.pc +%{_datadir}/gir-1.0/Vte-3.91.gir +%doc %{_docdir}/vte-2.91-gtk4/ +%{_datadir}/vala/vapi/vte-2.91-gtk4.deps +%{_datadir}/vala/vapi/vte-2.91-gtk4.vapi + +%files -n vte-profile +%license COPYING.GPL3 +%{_libexecdir}/vte-urlencode-cwd +%{_sysconfdir}/profile.d/vte.csh +%{_sysconfdir}/profile.d/vte.sh + +%changelog +* Tue Nov 26 2024 MSVSphere Packaging Team - 0.76.3-2 +- Rebuilt for MSVSphere 10 + +* Mon Jun 24 2024 Troy Dawson - 0.76.3-2 +- Bump release for June 2024 mass rebuild + +* Mon Jun 10 2024 David King - 0.76.3-1 +- Update to 0.76.3 + +* Fri Jun 07 2024 David King - 0.76.2-2 +- Use updated notification patches from ptyxis + +* Tue May 28 2024 David King - 0.76.2-1 +- Update to 0.76.2 + +* Fri May 03 2024 David King - 0.76.1-1 +- Update to 0.76.1 + +* Tue Apr 02 2024 David King - 0.76.0-1 +- Update to 0.76.0 + +* Mon Feb 12 2024 Tomas Popela - 0.74.2-4 +- Build for the SPDX license format change + +* Wed Jan 31 2024 Pete Walter - 0.74.2-3 +- Rebuild for ICU 74 + +* Sat Jan 27 2024 Fedora Release Engineering - 0.74.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Sat Dec 16 2023 Kalev Lember - 0.74.2-1 +- Update to 0.74.2 + +* Sun Oct 22 2023 Kalev Lember - 0.74.1-1 +- Update to 0.74.1 + +* Tue Sep 19 2023 Kalev Lember - 0.74.0-1 +- Update to 0.74.0 + +* Tue Aug 08 2023 Kalev Lember - 0.73.93-1 +- Update to 0.73.93 + +* Sat Jul 22 2023 Fedora Release Engineering - 0.72.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + +* Tue Jul 11 2023 František Zatloukal - 0.72.2-2 +- Rebuilt for ICU 73.2 + +* Wed Jun 07 2023 Kalev Lember - 0.72.2-1 +- Update to 0.72.2 + +* Sun Apr 16 2023 David King - 0.72.1-1 +- Update to 0.72.1 + +* Mon Mar 20 2023 David King - 0.72.0-1 +- Update to 0.72.0 (#2179642) + +* Thu Mar 09 2023 David King - 0.71.99-1 +- Update to 0.71.99 + +* Wed Feb 15 2023 David King - 0.71.92-1 +- Update to 0.71.92 + +* Sat Jan 21 2023 Fedora Release Engineering - 0.70.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + +* Sat Dec 31 2022 Pete Walter - 0.70.2-2 +- Rebuild for ICU 72 + +* Tue Dec 06 2022 David King - 0.70.2-1 +- Update to 0.70.2 + +* Fri Oct 28 2022 David King - 0.70.1-1 +- Update to 0.70.1 + +* Mon Sep 26 2022 David King - 0.70.0-2 +- Fix GTK4 ABI padding (#2122922) + +* Mon Sep 19 2022 Kalev Lember - 0.70.0-1 +- Update to 0.70.0 + +* Mon Aug 08 2022 Kalev Lember - 0.69.92-1 +- Update to 0.69.92 + +* Wed Aug 03 2022 David King - 0.69.90-1 +- Update to 0.69.90 +- Enable GTK4 support + +* Mon Aug 01 2022 Frantisek Zatloukal - 0.68.0-3 +- Rebuilt for ICU 71.1 + +* Sat Jul 23 2022 Fedora Release Engineering - 0.68.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Sun Mar 27 2022 David King - 0.68.0-1 +- Update to 0.68.0 + +* Thu Feb 17 2022 David King - 0.67.90-1 +- Update to 0.67.90 + +* Thu Jan 27 2022 David King - 0.66.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Thu Dec 16 2021 David King - 0.66.2-1 +- Update to 0.66.2 + +* Mon Nov 01 2021 David King - 0.66.1-1 +- Update to 0.66.1 + +* Fri Oct 01 2021 Kalev Lember - 0.66.0-2 +- Require systemd-libs rather than systemd + +* Tue Sep 28 2021 David King - 0.66.0-1 +- Update to 0.66.0 + +* Fri Jul 23 2021 Fedora Release Engineering - 0.64.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Thu Jun 17 2021 Debarshi Ray - 0.64.2-2 +- Fix the License fields and ship the correct license texts + +* Wed Jun 16 2021 Debarshi Ray - 0.64.2-1 +- Update to 0.64.2 + +* Thu May 20 2021 Pete Walter - 0.64.1-3 +- Rebuild for ICU 69 + +* Fri May 07 2021 Debarshi Ray - 0.64.1-2 +- Add missing _VTE_CXX_NOEXCEPT in downstream patches + +* Thu May 06 2021 Debarshi Ray - 0.64.1-1 +- Update to 0.64.1 + +* Thu May 06 2021 Debarshi Ray - 0.64.0-1 +- Update to 0.64.0 + +* Thu May 06 2021 Debarshi Ray - 0.63.91-1 +- Update to 0.63.91 +- Rebase downstream patches + +* Thu Feb 18 2021 Kalev Lember - 0.62.3-2 +- Revert a change that limited select all, as decided by Workstation WG + +* Tue Feb 16 2021 Kalev Lember - 0.62.3-1 +- Update to 0.62.3 +- Use https URLs for upstream + +* Wed Jan 27 2021 Fedora Release Engineering - 0.62.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Mon Jan 25 2021 Kalev Lember - 0.62.2-1 +- Update to 0.62.2 + +* Wed Dec 16 2020 Debarshi Ray - 0.62.1-3 +- Accommodate 'sudo toolbox' when tracking the active container + +* Tue Nov 03 2020 Jeff Law - 0.62.1-2 +- Fix bogus volatile caught by gcc-11 + +* Thu Oct 08 2020 Debarshi Ray - 0.62.1-1 +- Update to 0.62.1 +- Rebase downstream patches + +* Thu Sep 24 2020 Debarshi Ray - 0.62.0-1 +- Update to 0.62.0 + +* Thu Sep 24 2020 Debarshi Ray - 0.61.91-1 +- Update to 0.61.91 + +* Thu Sep 24 2020 Debarshi Ray - 0.61.90-1 +- Update to 0.61.90 +- Rebase downstream patches + +* Wed Jul 29 2020 Fedora Release Engineering - 0.60.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Thu Jun 04 2020 Kalev Lember - 0.60.3-1 +- Update to 0.60.3 + +* Sat May 16 2020 Pete Walter - 0.60.2-2 +- Rebuild for ICU 67 + +* Mon Apr 27 2020 Kalev Lember - 0.60.2-1 +- Update to 0.60.2 + +* Mon Apr 06 2020 Debarshi Ray - 0.60.1-2 +- Improve legibility when using colours from the system theme + +* Tue Mar 31 2020 Kalev Lember - 0.60.1-1 +- Update to 0.60.1 + +* Sat Mar 21 2020 Kalev Lember - 0.60.0-2 +- Move vte-urlencode-cwd to vte-profile subpackage (#1815769) + +* Fri Mar 06 2020 Debarshi Ray - 0.60.0-1 +- Update to 0.60.0 + +* Mon Mar 02 2020 Debarshi Ray - 0.59.92-2 +- Replace C1 controls with C0 to emit OSC 777 from PS0 (RH #1783802) + +* Mon Mar 02 2020 Debarshi Ray - 0.59.92-1 +- Update to 0.59.92 + +* Thu Feb 20 2020 Debarshi Ray - 0.59.91-1 +- Update to 0.59.91 +- Rebase downstream patches + +* Wed Feb 19 2020 Debarshi Ray - 0.59.0-1 +- Update to 0.59.0 +- Rebase downstream patches + +* Fri Jan 31 2020 Fedora Release Engineering - 0.58.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Wed Nov 27 2019 Kalev Lember - 0.58.3-1 +- Update to 0.58.3 +- Avoid overriding vte's own -fno-exceptions + +* Mon Oct 14 2019 Kalev Lember - 0.58.2-1 +- Update to 0.58.2 + +* Mon Oct 07 2019 Kalev Lember - 0.58.1-1 +- Update to 0.58.1 + +* Fri Oct 04 2019 Adam Williamson - 0.58.0-2 +- Backport fix for crash due to out of bounds cursor position (#1756567) + +* Mon Sep 09 2019 Kalev Lember - 0.58.0-1 +- Update to 0.58.0 + +* Mon Aug 12 2019 Kalev Lember - 0.57.90-1 +- Update to 0.57.90 + +* Sat Jul 27 2019 Fedora Release Engineering - 0.57.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Tue Jul 02 2019 Debarshi Ray - 0.57.3-1 +- Update to 0.57.3 +- Rebase downstream patches + +* Wed Jun 19 2019 Debarshi Ray - 0.57.0-2 +- Support tracking the active container inside the terminal + +* Tue Jun 18 2019 Debarshi Ray - 0.57.0-1 +- Update to 0.57.0 +- Switch to the Meson build system +- Rebase downstream patches + +* Tue May 07 2019 Kalev Lember - 0.56.3-1 +- Update to 0.56.3 + +* Mon May 06 2019 Kalev Lember - 0.56.2-1 +- Update to 0.56.2 + +* Tue Apr 09 2019 Kalev Lember - 0.56.1-1 +- Update to 0.56.1 + +* Tue Apr 02 2019 Debarshi Ray - 0.56.0-2 +- Add signals proxying an interactive shell's precmd and preexec hooks. + +* Mon Mar 11 2019 Kalev Lember - 0.56.0-1 +- Update to 0.56.0 + +* Mon Mar 04 2019 Kalev Lember - 0.55.92-1 +- Update to 0.55.92 + +* Tue Feb 19 2019 Kalev Lember - 0.55.90-2 +- Rebuilt against fixed atk (#1626575) + +* Tue Feb 19 2019 Kalev Lember - 0.55.90-1 +- Update to 0.55.90 + +* Sun Feb 03 2019 Fedora Release Engineering - 0.54.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Wed Dec 12 2018 Kalev Lember - 0.54.3-1 +- Update to 0.54.3 + +* Fri Oct 26 2018 Kalev Lember - 0.54.2-1 +- Update to 0.54.2 + +* Mon Oct 08 2018 Debarshi Ray - 0.54.1-4 +- Removal of utmp logging makes the utmp group unnecessary + +* Fri Oct 05 2018 Debarshi Ray - 0.54.1-3 +- Tweak the escape sequence emission to unbreak the parsing + +* Fri Oct 05 2018 Debarshi Ray - 0.54.1-2 +- Tighten the dependencies a bit + +* Fri Oct 05 2018 Debarshi Ray - 0.54.1-1 +- Update to 0.54.1 + +* Thu Oct 04 2018 Debarshi Ray - 0.54.0-1 +- Update to 0.54.0 + +* Thu Oct 04 2018 Debarshi Ray - 0.53.92-1 +- Update to 0.53.92 + +* Sat Jul 14 2018 Fedora Release Engineering - 0.53.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Mon Jun 18 2018 Kalev Lember - 0.53.0-2 +- Require systemd, not initscripts for the utmp group (#1592403) + +* Mon Jun 04 2018 Debarshi Ray - 0.53.0-1 +- Update to 0.53.0 + +* Mon May 21 2018 Kalev Lember - 0.52.2-1 +- Update to 0.52.2 + +* Mon Apr 09 2018 Kalev Lember - 0.52.1-1 +- Update to 0.52.1 + +* Tue Apr 03 2018 Kalev Lember - 0.52.0-1 +- Update to 0.52.0 +- Remove ldconfig scriptlets + +* Wed Mar 28 2018 Debarshi Ray - 0.51.90-1 +- Update to 0.51.90 + +* Wed Mar 28 2018 Debarshi Ray - 0.51.3-1 +- Update to 0.51.3 +- Rebase downstream patches + +* Thu Feb 08 2018 Igor Gnatenko - 0.50.2-3 +- Switch to %%ldconfig_scriptlets + +* Thu Nov 02 2017 Kalev Lember - 0.50.2-2 +- Rebuild + +* Wed Nov 01 2017 Debarshi Ray - 0.50.2-1 +- Update to 0.50.2 + +* Thu Oct 05 2017 Debarshi Ray - 0.50.1-1 +- Update to 0.50.1 +- Rebase downstream patches + +* Thu Sep 14 2017 Kalev Lember - 0.50.0-1 +- Update to 0.50.0 +- Rebase downstream patches + +* Thu Aug 03 2017 Fedora Release Engineering - 0.48.3-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 0.48.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Wed May 10 2017 Kalev Lember - 0.48.3-1 +- Update to 0.48.3 + +* Wed Apr 12 2017 Kalev Lember - 0.48.2-1 +- Update to 0.48.2 +- Rebase downstream patches + +* Wed Mar 22 2017 Kalev Lember - 0.48.1-1 +- Update to 0.48.1 + +* Fri Feb 24 2017 Debarshi Ray - 0.47.90-1 +- Update to 0.47.90 +- Rebase downstream patches + +* Sat Feb 11 2017 Fedora Release Engineering - 0.46.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Tue Nov 08 2016 Debarshi Ray - 0.46.1-1 +- Update to 0.46.1 +- Rebase downstream patches + +* Thu Sep 22 2016 Kalev Lember - 0.46.0-2 +- BR vala instead of obsolete vala-tools subpackage + +* Mon Sep 19 2016 Kalev Lember - 0.46.0-1 +- Update to 0.46.0 + +* Wed Sep 14 2016 Kalev Lember - 0.45.92-1 +- Update to 0.45.92 + +* Thu Aug 18 2016 Kalev Lember - 0.45.90-1 +- Update to 0.45.90 +- Rebase downstream patches + +* Fri Jul 01 2016 Debarshi Ray - 0.44.2-2 +- Add a property to configure the scroll speed + +* Tue May 10 2016 Debarshi Ray - 0.44.2-1 +- Update to 0.44.2 +- Rebase downstream patches and undo unintentional ABI break + +* Mon Apr 11 2016 Debarshi Ray - 0.44.1-1 +- Update to 0.44.1 + +* Tue Mar 22 2016 Kalev Lember - 0.44.0-1 +- Update to 0.44.0 + +* Tue Mar 15 2016 Debarshi Ray - 0.43.92-1 +- Update to 0.43.92 + +* Tue Mar 01 2016 Debarshi Ray - 0.43.91-1 +- Update to 0.43.91 +- Remove BuildRequires on pkgconfig(libpcre2-8) + +* Tue Mar 01 2016 Debarshi Ray - 0.43.90-1 +- Update to 0.43.90 + +* Fri Feb 05 2016 Fedora Release Engineering - 0.43.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Fri Jan 29 2016 Debarshi Ray - 0.43.2-1 +- Update to 0.43.2 + +* Fri Jan 29 2016 Debarshi Ray - 0.43.1-1 +- Update to 0.43.1 +- Drop upstreamed patch + +* Fri Jan 29 2016 Debarshi Ray - 0.43.0-1 +- Update to 0.43.0 +- Add BuildRequires on pkgconfig(libpcre2-8) +- Disable -Wnonnull + +* Thu Jan 28 2016 Debarshi Ray - 0.42.3-1 +- Update to 0.42.3 +- Backport upstream patch to fix disappearing lines (GNOME #761097) + +* Wed Oct 14 2015 Kalev Lember - 0.42.1-1 +- Update to 0.42.1 + +* Tue Sep 22 2015 Kalev Lember - 0.42.0-1 +- Update to 0.42.0 +- Use license macro for COPYING + +* Mon Sep 14 2015 Debarshi Ray - 0.41.90-1 +- Update to 0.41.90 +- Rebased downstream patches after the migration to C++ +- gnome-pty-helper has been removed + +* Fri Jun 19 2015 Fedora Release Engineering - 0.40.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Tue May 12 2015 Debarshi Ray - 0.40.2-1 +- Update to 0.40.2 + +* Tue Mar 24 2015 Debarshi Ray - 0.40.0-1 +- Update to 0.40.0 + +* Thu Mar 19 2015 Debarshi Ray - 0.39.92-1 +- Update to 0.39.92 + +* Tue Feb 17 2015 Debarshi Ray - 0.39.90-1 +- Update to 0.39.90 +- Add command-notify patches + +* Fri Dec 19 2014 Richard Hughes - 0.39.1-1 +- Update to 0.39.1 + +* Mon Dec 01 2014 Debarshi Ray - 0.39.0-2 +- Backport upstream patch to fix zombie shells (GNOME #740929) + +* Tue Nov 25 2014 Kalev Lember - 0.39.0-1 +- Update to 0.39.0 + +* Mon Nov 10 2014 Kalev Lember - 0.38.2-1 +- Update to 0.38.2 + +* Mon Oct 13 2014 Kalev Lember - 0.38.1-1 +- Update to 0.38.1 + +* Sun Sep 14 2014 Kalev Lember - 0.38.0-1 +- Update to 0.38.0 + +* Mon Aug 18 2014 Kalev Lember - 0.37.90-1 +- Update to 0.37.90 + +* Mon Aug 18 2014 Fedora Release Engineering - 0.37.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Tue Jul 22 2014 Kalev Lember - 0.37.2-2 +- Rebuilt for gobject-introspection 1.41.4 + +* Tue Jun 24 2014 Richard Hughes - 0.37.2-1 +- Update to 0.37.2 + +* Sun Jun 08 2014 Fedora Release Engineering - 0.37.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Wed May 28 2014 Kalev Lember - 0.37.1-1 +- Update to 0.37.1 + +* Wed May 07 2014 Kalev Lember - 0.37.0-2 +- Split out a vte-profile subpackage that can be used with both vte291 / vte3 + +* Tue May 06 2014 Kalev Lember - 0.37.0-1 +- Initial Fedora package, based on previous vte3 0.36 packaging