[vte] parser: Implement OSC parsing
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vte] parser: Implement OSC parsing
- Date: Tue, 27 Mar 2018 17:44:29 +0000 (UTC)
commit c4cd94d2b5ef472d83888a320c0d3744561d5ecc
Author: Christian Persch <chpe src gnome org>
Date: Tue Mar 27 19:40:12 2018 +0200
parser: Implement OSC parsing
src/Makefile.am | 6 +
src/parser-arg.hh | 2 +-
src/parser-cat.cc | 24 ++-
src/parser-cmd.hh | 1 +
src/parser-glue.hh | 324 ++++++++++++++++-----
src/parser-osc.hh | 100 +++++++
src/parser-string.hh | 138 +++++++++
src/parser-test.cc | 393 +++++++++++++++++++++++--
src/parser.cc | 58 +++--
src/parser.hh | 12 +-
src/vte.cc | 53 ++--
src/vtegtk.cc | 10 +-
src/vteinternal.hh | 63 +++--
src/vteseq.cc | 808 +++++++++++++++++++++++++-------------------------
14 files changed, 1399 insertions(+), 593 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index a17d441..e93f6a5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -65,6 +65,8 @@ libvte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_la_SOURCES = \
parser-charset-tables.hh \
parser-cmd.hh \
parser-glue.hh \
+ parser-osc.hh \
+ parser-string.hh \
pty.cc \
reaper.cc \
reaper.hh \
@@ -248,6 +250,8 @@ parser_cat_SOURCES = \
parser-charset-tables.hh \
parser-cmd.hh \
parser-glue.hh \
+ parser-osc.hh \
+ parser-string.hh \
parser-cat.cc \
vteconv.cc \
vteconv.h \
@@ -281,6 +285,8 @@ test_parser_SOURCES = \
parser-charset.hh \
parser-charset-tables.hh \
parser-glue.hh \
+ parser-osc.hh \
+ parser-string.hh \
$(NULL)
test_parser_CPPFLAGS = \
-I$(builddir) \
diff --git a/src/parser-arg.hh b/src/parser-arg.hh
index 88a31b2..0f94832 100644
--- a/src/parser-arg.hh
+++ b/src/parser-arg.hh
@@ -103,7 +103,7 @@ static inline void vte_seq_arg_push(vte_seq_arg_t* arg,
* @arg:
* @finalise:
*
- * Finished @arg; after this no more vte_seq_arg_push() calls
+ * Finishes @arg; after this no more vte_seq_arg_push() calls
* are allowed.
*
* If @nonfinal is %true, marks @arg as a nonfinal parameter, is,
diff --git a/src/parser-cat.cc b/src/parser-cat.cc
index 88319a6..6953097 100644
--- a/src/parser-cat.cc
+++ b/src/parser-cat.cc
@@ -169,6 +169,19 @@ print_intermediates(GString* str,
}
static void
+print_string(GString* str,
+ struct vte_seq const* seq)
+{
+ size_t len;
+ auto buf = vte_seq_string_get(&seq->arg_str, &len);
+
+ g_string_append_c(str, '\"');
+ for (size_t i = 0; i < len; ++i)
+ g_string_append_unichar(str, buf[i]);
+ g_string_append_c(str, '\"');
+}
+
+static void
print_seq_and_params(GString* str,
const struct vte_seq *seq,
bool plain)
@@ -238,12 +251,19 @@ print_seq(GString* str,
}
case VTE_SEQ_CSI:
- case VTE_SEQ_DCS:
- case VTE_SEQ_OSC: {
+ case VTE_SEQ_DCS: {
print_seq_and_params(str, seq, plain);
break;
}
+ case VTE_SEQ_OSC: {
+ printer p(str, plain, SEQ_START, SEQ_END);
+ g_string_append(str, "{OSC ");
+ print_string(str, seq);
+ g_string_append_c(str, '}');
+ break;
+ }
+
default:
assert(false);
}
diff --git a/src/parser-cmd.hh b/src/parser-cmd.hh
index a94a308..ea2dbcb 100644
--- a/src/parser-cmd.hh
+++ b/src/parser-cmd.hh
@@ -152,6 +152,7 @@ _VTE_CMD(MC_DEC) /* media-copy-dec */
_VTE_CMD(NEL) /* next-line */
_VTE_CMD(NP) /* next-page */
_VTE_CMD(NUL) /* nul */
+_VTE_CMD(OSC) /* operating-system-command */
_VTE_CMD(PP) /* preceding-page */
_VTE_CMD(PPA) /* page-position-absolute */
_VTE_CMD(PPB) /* page-position-backward */
diff --git a/src/parser-glue.hh b/src/parser-glue.hh
index 98ef424..5e4303e 100644
--- a/src/parser-glue.hh
+++ b/src/parser-glue.hh
@@ -19,6 +19,7 @@
#include <cstdint>
#include <algorithm>
+#include <string>
#include "parser.hh"
@@ -31,7 +32,8 @@ public:
typedef int number;
- char* ucs4_to_utf8(gunichar const* str) const noexcept;
+ char* ucs4_to_utf8(gunichar const* str,
+ ssize_t len = -1) const noexcept;
void print() const noexcept;
@@ -91,6 +93,53 @@ public:
return m_seq->terminator;
}
+ // FIXMEchpe: upgrade to C++17 and use the u32string_view version below, instead
+ /*
+ * string:
+ *
+ * This is the string argument of a DCS or OSC sequence.
+ *
+ * Returns: the string argument
+ */
+ inline std::u32string string() const noexcept
+ {
+ size_t len;
+ auto buf = vte_seq_string_get(&m_seq->arg_str, &len);
+ return std::u32string(reinterpret_cast<char32_t*>(buf), len);
+ }
+
+ #if 0
+ /*
+ * string:
+ *
+ * This is the string argument of a DCS or OSC sequence.
+ *
+ * Returns: the string argument
+ */
+ inline constexpr std::u32string_view string() const noexcept
+ {
+ size_t len = 0;
+ auto buf = vte_seq_string_get(&m_seq->arg_str, &len);
+ return std::u32string_view(buf, len);
+ }
+ #endif
+
+ /*
+ * string:
+ *
+ * This is the string argument of a DCS or OSC sequence.
+ *
+ * Returns: the string argument
+ */
+ std::string string_utf8() const noexcept;
+
+ inline char* string_param() const noexcept
+ {
+ size_t len = 0;
+ auto buf = vte_seq_string_get(&m_seq->arg_str, &len);
+ return ucs4_to_utf8(buf, len);
+ }
+
/* size:
*
* Returns: the number of parameters
@@ -274,105 +323,232 @@ public:
return idx <= next(start_idx);
}
- //FIMXE remove this one
- inline constexpr int operator[](int position) const
- {
- return __builtin_expect(position < (int)size(), 1) ?
vte_seq_arg_value(m_seq->args[position]) : -1;
- }
+ struct vte_seq** seq_ptr() { return &m_seq; }
+
+ inline explicit operator bool() const { return m_seq != nullptr; }
+
+private:
+ struct vte_seq *m_seq{nullptr};
- inline bool has_number_at_unchecked(unsigned int position) const
+ char const* type_string() const;
+ char const* command_string() const;
+};
+
+class StringTokeniser {
+public:
+ using string_type = std::string;
+ using char_type = std::string::value_type;
+
+private:
+ string_type const& m_string;
+ char_type m_separator{';'};
+
+public:
+ StringTokeniser(string_type const& s,
+ char_type separator = ';')
+ : m_string{s},
+ m_separator{separator}
{
- return true;
}
- inline bool number_at_unchecked(unsigned int position, number& v) const
+ StringTokeniser(string_type&& s,
+ char_type separator = ';')
+ : m_string{s},
+ m_separator{separator}
{
- v = vte_seq_arg_value(m_seq->args[position]);
- return true;
}
- inline bool number_at(unsigned int position, number& v) const
- {
- if (G_UNLIKELY(position >= size()))
- return false;
+ StringTokeniser(StringTokeniser const&) = delete;
+ StringTokeniser(StringTokeniser&&) = delete;
+ ~StringTokeniser() = default;
- return number_at_unchecked(position, v);
- }
+ StringTokeniser& operator=(StringTokeniser const&) = delete;
+ StringTokeniser& operator=(StringTokeniser&&) = delete;
- inline number number_or_default_at_unchecked(unsigned int position, number default_v = 0) const
- {
- number v;
- if (G_UNLIKELY(!number_at_unchecked(position, v)))
- v = default_v;
- return v;
- }
+ /*
+ * const_iterator:
+ *
+ * InputIterator for string tokens.
+ */
+ class const_iterator {
+ public:
+ using difference_type = ptrdiff_t;
+ using value_type = string_type;
+ using pointer = string_type;
+ using reference = string_type;
+ using iterator_category = std::input_iterator_tag;
+ using size_type = string_type::size_type;
+
+ private:
+ string_type const* m_string;
+ char_type m_separator{';'};
+ string_type::size_type m_position;
+ string_type::size_type m_next_separator;
+
+ public:
+ const_iterator(string_type const* str,
+ char_type separator,
+ size_type position)
+ : m_string{str},
+ m_separator{separator},
+ m_position{position},
+ m_next_separator{m_string->find(m_separator, m_position)}
+ {
+ }
+ const_iterator(string_type const* str,
+ char_type separator)
+ : m_string{str},
+ m_separator{separator},
+ m_position{string_type::npos},
+ m_next_separator{string_type::npos}
+ {
+ }
- inline number number_or_default_at(unsigned int position, number default_v = 0) const
- {
- number v;
- if (G_UNLIKELY(!number_at(position, v)))
- v = default_v;
- return v;
- }
+ const_iterator(const_iterator const&) = default;
+ const_iterator(const_iterator&& o)
+ : m_string{o.m_string},
+ m_separator{o.m_separator},
+ m_position{o.m_position},
+ m_next_separator{o.m_next_separator}
+ {
+ }
- inline bool string_at_unchecked(unsigned int position, char*& str) const
- {
-#if 0
- auto value = value_at_unchecked(position);
- if (G_LIKELY(G_VALUE_HOLDS_POINTER(value))) {
- str = ucs4_to_utf8((gunichar const*)g_value_get_pointer (value));
- return str != nullptr;
+ ~const_iterator() = default;
+
+ const_iterator& operator=(const_iterator const& o)
+ {
+ m_string = o.m_string;
+ m_separator = o.m_separator;
+ m_position = o.m_position;
+ m_next_separator = o.m_next_separator;
+ return *this;
}
- if (G_VALUE_HOLDS_STRING(value)) {
- /* Copy the string into the buffer. */
- str = g_value_dup_string(value);
- return str != nullptr;
+
+ const_iterator& operator=(const_iterator&& o)
+ {
+ m_string = std::move(o.m_string);
+ m_separator = o.m_separator;
+ m_position = o.m_position;
+ m_next_separator = o.m_next_separator;
+ return *this;
}
- if (G_VALUE_HOLDS_LONG(value)) {
- /* Convert the long to a string. */
- str = g_strdup_printf("%ld", g_value_get_long(value));
- return true;
+
+ inline bool operator==(const_iterator const& o) const noexcept
+ {
+ return m_position == o.m_position;
+ }
+
+ inline bool operator!=(const_iterator const& o) const noexcept
+ {
+ return m_position != o.m_position;
+ }
+
+ inline const_iterator& operator++() noexcept
+ {
+ if (m_next_separator != string_type::npos) {
+ m_position = ++m_next_separator;
+ m_next_separator = m_string->find(m_separator, m_position);
+ } else
+ m_position = string_type::npos;
+
+ return *this;
+ }
+
+ /*
+ * number:
+ *
+ * Returns the value of the iterator as a number, or -1
+ * if the string could not be parsed as a number, or
+ * the parsed values exceeds the uint16_t range.
+ *
+ * Returns: true if a number was parsed
+ */
+ bool number(int& v) const noexcept
+ {
+ auto const s = size();
+ if (s == 0) {
+ v = -1;
+ return true;
+ }
+
+ v = 0;
+ size_type i;
+ for (i = 0; i < s; ++i) {
+ char_type c = (*m_string)[m_position + i];
+ if (c < '0' || c > '9')
+ return false;
+
+ v = v * 10 + (c - '0');
+ if (v > 0xffff)
+ return false;
+ }
+
+ /* All consumed? */
+ return i == s;
+ }
+
+ inline size_type size() const noexcept
+ {
+ if (m_next_separator != string_type::npos)
+ return m_next_separator - m_position;
+ else
+ return m_string->size() - m_position;
+ }
+
+ inline size_type size_remaining() const noexcept
+ {
+ return m_string->size() - m_position;
}
-#endif
- str = nullptr;
- return false;
- }
- inline bool string_at(unsigned int position, char*& str) const
+ inline string_type operator*() const noexcept
+ {
+ return m_string->substr(m_position, size());
+ }
+
+ /*
+ * string_remaining:
+ *
+ * Returns the whole string left, including possibly more separators.
+ */
+ inline string_type string_remaining() const noexcept
+ {
+ return m_string->substr(m_position);
+ }
+
+ inline void append(string_type& str) const noexcept
+ {
+ str.append(m_string->substr(m_position, size()));
+ }
+
+ inline void append_remaining(string_type& str) const noexcept
+ {
+ str.append(m_string->substr(m_position));
+ }
+
+ }; // class const_iterator
+
+ inline const_iterator cbegin(char_type c = ';') const noexcept
{
-#if 0
- if (G_UNLIKELY(position >= size()))
- return false;
-
- return string_at_unchecked(position, str);
-#endif
- str = nullptr;
- return false;
+ return const_iterator(&m_string, m_separator, 0);
}
- inline bool has_subparams_at_unchecked(unsigned int position) const
+ inline const_iterator cend() const noexcept
{
- return false;
+ return const_iterator(&m_string, m_separator);
}
- inline Sequence subparams_at_unchecked(unsigned int position) const
+ inline const_iterator begin(char_type c = ';') const noexcept
{
- return Sequence{};
+ return cbegin();
}
- struct vte_seq** seq_ptr() { return &m_seq; }
-
- inline explicit operator bool() const { return m_seq != nullptr; }
-
-private:
- struct vte_seq *m_seq{nullptr};
-
- char const* type_string() const;
- char const* command_string() const;
-};
+ inline const_iterator end() const noexcept
+ {
+ return cend();
+ }
-typedef Sequence Params;
+}; // class StringTokeniser
} // namespace parser
diff --git a/src/parser-osc.hh b/src/parser-osc.hh
new file mode 100644
index 0000000..ca2cdc8
--- /dev/null
+++ b/src/parser-osc.hh
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2018 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License) or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful)
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not) see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(_VTE_OSC)
+#error "Must define _VTE_OSC before including this file"
+#endif
+
+/* _VTE_OSC(DTTERM_CWD, 3) Conflicts with XTERM_SET_XPROPERTY */
+
+_VTE_OSC(EMACS_51, 51)
+
+_VTE_OSC(ITERM2_GROWL, 9)
+_VTE_OSC(ITERM2_133, 133)
+_VTE_OSC(ITERM2_1337, 1337)
+
+_VTE_OSC(KONSOLE_30, 30)
+_VTE_OSC(KONSOLE_31, 31)
+
+/* _VTE_OSC(MINTTY_CLIPBOARD_COPY_WINDOW_TITLE, 7721) */
+/* _VTE_OSC(MINTTY_CHANGE_FONT_SIZE, 7770) */
+/* _VTE_OSC(MINTTY_QUERY_FONT_SUPPORTS_CHARACTERS, 7771) */
+/* _VTE_OSC(MINTTY_CHANGE_FONT_AND_WINDOW_SIZE, 7777) */
+/* _VTE_OSC(MINTTY_INDIC_WIDE, 77119) out of range */
+
+_VTE_OSC(RLOGIN_SET_KANJI_MODE, 800)
+_VTE_OSC(RLOGIN_SPEECH, 801)
+
+/* _VTE_OSC(RXVT_MENU, 10) * Conflics with XTERM_SET_COLOR_TEXT_FG */
+_VTE_OSC(RXVT_SET_BACKGROUND_PIXMAP, 20)
+_VTE_OSC(RXVT_SET_COLOR_FG, 39)
+_VTE_OSC(RXVT_SET_COLOR_BG, 49)
+_VTE_OSC(RXVT_DUMP_SCREEN, 55)
+
+_VTE_OSC(URXVT_SET_LOCALE, 701)
+_VTE_OSC(URXVT_VERSION, 702)
+_VTE_OSC(URXVT_SET_COLOR_TEXT_ITALIC, 704)
+_VTE_OSC(URXVT_SET_COLOR_TEXT_BOLD, 706)
+_VTE_OSC(URXVT_SET_COLOR_UNDERLINE, 707)
+_VTE_OSC(URXVT_SET_COLOR_BORDER, 708)
+_VTE_OSC(URXVT_SET_FONT, 710)
+_VTE_OSC(URXVT_SET_FONT_BOLD, 711)
+_VTE_OSC(URXVT_SET_FONT_ITALIC, 712)
+_VTE_OSC(URXVT_SET_FONT_BOLD_ITALIC, 713)
+_VTE_OSC(URXVT_VIEW_UP, 720)
+_VTE_OSC(URXVT_VIEW_DOWN, 721)
+_VTE_OSC(URXVT_EXTENSION, 777)
+
+_VTE_OSC(VTECWF, 6)
+_VTE_OSC(VTECWD, 7)
+_VTE_OSC(VTEHYPER, 8)
+
+_VTE_OSC(XTERM_SET_WINDOW_AND_ICON_TITLE, 0)
+_VTE_OSC(XTERM_SET_ICON_TITLE, 1)
+_VTE_OSC(XTERM_SET_WINDOW_TITLE, 2)
+_VTE_OSC(XTERM_SET_XPROPERTY, 3)
+_VTE_OSC(XTERM_SET_COLOR, 4)
+_VTE_OSC(XTERM_SET_COLOR_SPECIAL, 5)
+/* _VTE_OSC(XTERM_SET_COLOR_MODE, 6) Conflict with our own OSC 6 VTECWF; so use 106 */
+_VTE_OSC(XTERM_SET_COLOR_TEXT_FG, 10)
+_VTE_OSC(XTERM_SET_COLOR_TEXT_BG, 11)
+_VTE_OSC(XTERM_SET_COLOR_CURSOR_BG, 12)
+_VTE_OSC(XTERM_SET_COLOR_MOUSE_CURSOR_FG, 13)
+_VTE_OSC(XTERM_SET_COLOR_MOUSE_CURSOR_BG, 14)
+_VTE_OSC(XTERM_SET_COLOR_TEK_FG, 15)
+_VTE_OSC(XTERM_SET_COLOR_TEK_BG, 16)
+_VTE_OSC(XTERM_SET_COLOR_HIGHLIGHT_BG, 17)
+_VTE_OSC(XTERM_SET_COLOR_TEK_CURSOR, 18)
+_VTE_OSC(XTERM_SET_COLOR_HIGHLIGHT_FG, 19)
+_VTE_OSC(XTERM_LOGFILE, 46)
+_VTE_OSC(XTERM_SET_FONT, 50)
+_VTE_OSC(XTERM_SET_XSELECTION, 52)
+_VTE_OSC(XTERM_RESET_COLOR, 104)
+_VTE_OSC(XTERM_RESET_COLOR_SPECIAL, 105)
+_VTE_OSC(XTERM_SET_COLOR_MODE, 106)
+_VTE_OSC(XTERM_RESET_COLOR_TEXT_FG, 110)
+_VTE_OSC(XTERM_RESET_COLOR_TEXT_BG, 111)
+_VTE_OSC(XTERM_RESET_COLOR_CURSOR_BG, 112)
+_VTE_OSC(XTERM_RESET_COLOR_MOUSE_CURSOR_FG, 113)
+_VTE_OSC(XTERM_RESET_COLOR_MOUSE_CURSOR_BG, 114)
+_VTE_OSC(XTERM_RESET_COLOR_TEK_FG, 115)
+_VTE_OSC(XTERM_RESET_COLOR_TEK_BG, 116)
+_VTE_OSC(XTERM_RESET_COLOR_HIGHLIGHT_BG, 117)
+_VTE_OSC(XTERM_RESET_COLOR_TEK_CURSOR, 118)
+_VTE_OSC(XTERM_RESET_COLOR_HIGHLIGHT_FG, 119)
+
+_VTE_OSC(YF_RQGWR, 8900)
diff --git a/src/parser-string.hh b/src/parser-string.hh
new file mode 100644
index 0000000..7de37e4
--- /dev/null
+++ b/src/parser-string.hh
@@ -0,0 +1,138 @@
+/*
+ * Copyright © 2018 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib.h>
+#include <assert.h>
+#include <cstdint>
+#include <cstring>
+
+/*
+ * vte_seq_string_t:
+ *
+ * A type to hold the argument string of a DSC or OSC sequence.
+ */
+typedef struct vte_seq_string_t {
+ uint32_t capacity;
+ uint32_t len;
+ uint32_t* buf;
+} vte_seq_string_t;
+
+#define VTE_SEQ_STRING_DEFAULT_CAPACITY (1 << 7) /* must be power of two */
+#define VTE_SEQ_STRING_MAX_CAPACITY (1 << 12)
+
+/*
+ * vte_seq_string_init:
+ *
+ * Returns: a new #vte_seq_string_t
+ */
+static inline void vte_seq_string_init(vte_seq_string_t* str) noexcept
+{
+ str->capacity = VTE_SEQ_STRING_DEFAULT_CAPACITY;
+ str->len = 0;
+ str->buf = (uint32_t*)g_malloc0_n(str->capacity, sizeof(uint32_t));
+}
+
+/*
+ * vte_seq_string_free:
+ * @string:
+ *
+ * Frees @string's storage and itself.
+ */
+static inline void vte_seq_string_free(vte_seq_string_t* str) noexcept
+{
+ g_free(str->buf);
+}
+
+/*
+ * vte_seq_string_ensure_capacity:
+ * @string:
+ *
+ * If @string's length is at capacity, and capacity is not maximal,
+ * expands the string's capacity.
+ *
+ * Returns: %true if the string has capacity for at least one more character
+ */
+static inline bool vte_seq_string_ensure_capacity(vte_seq_string_t* str) noexcept
+{
+ if (str->len < str->capacity)
+ return true;
+ if (str->capacity >= VTE_SEQ_STRING_MAX_CAPACITY)
+ return false;
+
+ str->capacity *= 2;
+ str->buf = (uint32_t*)g_realloc_n(str->buf, str->capacity, sizeof(uint32_t));
+ return true;
+}
+
+/*
+ * vte_seq_string_push:
+ * @string:
+ * @c: a character
+ *
+ * Appends @c to @str, or iff @str already has maximum length, does nothing.
+ *
+ * Returns: %true if the character was appended
+ */
+static inline bool vte_seq_string_push(vte_seq_string_t* str,
+ uint32_t c) noexcept
+{
+ if (!vte_seq_string_ensure_capacity(str))
+ return false;
+
+ str->buf[str->len++] = c;
+ return true;
+}
+
+/*
+ * vte_seq_string_finish:
+ * @string:
+ *
+ * Finishes @string; after this no more vte_seq_string_push() calls
+ * are allowed until the string is reset with vte_seq_string_reset().
+ */
+static inline void vte_seq_string_finish(vte_seq_string_t* str)
+{
+}
+
+/*
+ * vte_seq_string_reset:
+ * @string:
+ *
+ * Resets @string.
+ */
+static inline void vte_seq_string_reset(vte_seq_string_t* str) noexcept
+{
+ /* Zero length. However, don't clear the buffer, nor shrink the capacity. */
+ str->len = 0;
+}
+
+/*
+ * vte_seq_string_get:
+ * @string:
+ * @len: location to store the buffer length in code units
+ *
+ * Returns: the string's buffer as an array of uint32_t code units
+ */
+static constexpr inline uint32_t* vte_seq_string_get(vte_seq_string_t const* str,
+ size_t* len) noexcept
+{
+ assert(len != nullptr);
+ *len = str->len;
+ return str->buf;
+}
diff --git a/src/parser-test.cc b/src/parser-test.cc
index e050870..113719d 100644
--- a/src/parser-test.cc
+++ b/src/parser-test.cc
@@ -25,6 +25,8 @@
#include <string>
#include <vector>
+using namespace std::literals;
+
#include <glib.h>
#include "parser.hh"
@@ -155,6 +157,15 @@ public:
set_final(f);
}
+ vte_seq_builder(unsigned int type,
+ std::u32string const& str)
+ : m_arg_str(str)
+ {
+ memset(&m_seq, 0, sizeof(m_seq));
+ m_seq.type = type;
+ set_final(0);
+ }
+
~vte_seq_builder() = default;
void set_final(uint32_t raw) { m_seq.terminator = raw; }
@@ -189,8 +200,23 @@ public:
}
}
+ void set_string(std::u32string const& str)
+ {
+ m_arg_str = str;
+ }
+
+ enum VariantST {
+ ST_NONE,
+ ST_DEFAULT,
+ ST_C0,
+ ST_C1,
+ ST_BEL
+ };
+
void to_string(std::u32string& s,
- bool c1 = false);
+ bool c1 = false,
+ ssize_t max_arg_str_len = -1,
+ VariantST st = ST_DEFAULT);
void assert_equal(struct vte_seq* seq);
void assert_equal_full(struct vte_seq* seq);
@@ -201,25 +227,40 @@ private:
uint32_t m_i[4]{0, 0, 0, 0};
uint32_t m_p;
unsigned int m_ni{0};
+ std::u32string m_arg_str;
struct vte_seq m_seq;
-
};
void
vte_seq_builder::to_string(std::u32string& s,
- bool c1)
+ bool c1,
+ ssize_t max_arg_str_len,
+ VariantST st)
{
- switch (m_seq.type) {
- case VTE_SEQ_ESCAPE:
+ /* Introducer */
+ if (c1) {
+ switch (m_seq.type) {
+ case VTE_SEQ_ESCAPE: s.push_back(0x1B); break; // ESC
+ case VTE_SEQ_CSI: s.push_back(0x9B); break; // CSI
+ case VTE_SEQ_DCS: s.push_back(0x90); break; // DCS
+ case VTE_SEQ_OSC: s.push_back(0x9D); break; // OSC
+ default: return;
+ }
+ } else {
s.push_back(0x1B); // ESC
- break;
- case VTE_SEQ_CSI: {
- if (c1) {
- s.push_back(0x9B); // CSI
- } else {
- s.push_back(0x1B); // ESC
- s.push_back(0x5B); // [
+ switch (m_seq.type) {
+ case VTE_SEQ_ESCAPE: break; // nothing more
+ case VTE_SEQ_CSI: s.push_back(0x5B); break; // [
+ case VTE_SEQ_DCS: s.push_back(0x50); break; // P
+ case VTE_SEQ_OSC: s.push_back(0x5D); break; // ]
+ default: return;
}
+ }
+
+ /* Parameters */
+ switch (m_seq.type) {
+ case VTE_SEQ_CSI:
+ case VTE_SEQ_DCS: {
if (m_p != 0)
s.push_back(m_p);
@@ -238,13 +279,59 @@ vte_seq_builder::to_string(std::u32string& s,
break;
}
default:
- return;
+ break;
+ }
+
+ /* Intermediates and Final */
+ switch (m_seq.type) {
+ case VTE_SEQ_ESCAPE:
+ case VTE_SEQ_CSI:
+ case VTE_SEQ_DCS:
+ for (unsigned int n = 0; n < m_ni; n++)
+ s.push_back(m_i[n]);
+
+ s.push_back(m_seq.terminator);
+ break;
+ default:
+ break;
}
- for (unsigned int n = 0; n < m_ni; n++)
- s.push_back(m_i[n]);
+ /* String and ST */
+ switch (m_seq.type) {
+ case VTE_SEQ_DCS:
+ case VTE_SEQ_OSC:
+
+ if (max_arg_str_len < 0)
+ s.append(m_arg_str, 0, max_arg_str_len);
+ else
+ s.append(m_arg_str);
- s.push_back(m_seq.terminator);
+ switch (st) {
+ case ST_NONE:
+ // omit ST
+ break;
+ case ST_DEFAULT:
+ if (c1) {
+ s.push_back(0x9C); // ST
+ } else {
+ s.push_back(0x1B); // ESC
+ s.push_back(0x5C); // BACKSLASH
+ }
+ break;
+ case ST_C0:
+ s.push_back(0x1B); // ESC
+ s.push_back(0x5C); // BACKSLASH
+ break;
+ case ST_C1:
+ s.push_back(0x9C); // ST
+ break;
+ case ST_BEL:
+ s.push_back(0x7); // BEL
+ break;
+ default:
+ break;
+ }
+ }
}
void
@@ -259,7 +346,7 @@ void
vte_seq_builder::assert_equal(struct vte_seq* seq)
{
g_assert_cmpuint(m_seq.type, ==, seq->type);
- g_assert_cmpuint(m_seq.terminator, ==, seq->terminator);
+ g_assert_cmphex(m_seq.terminator, ==, seq->terminator);
}
void
@@ -330,6 +417,39 @@ test_seq_arg(void)
}
static void
+test_seq_string(void)
+{
+ vte_seq_string_t str;
+ vte_seq_string_init(&str);
+
+ size_t len;
+ auto buf = vte_seq_string_get(&str, &len);
+ g_assert_cmpuint(len, ==, 0);
+
+ for (unsigned int i = 0; i < VTE_SEQ_STRING_MAX_CAPACITY; ++i) {
+ auto rv = vte_seq_string_push(&str, 0xfffdU);
+ g_assert_true(rv);
+
+ buf = vte_seq_string_get(&str, &len);
+ g_assert_cmpuint(len, ==, i + 1);
+ }
+
+ /* Try one more */
+ auto rv = vte_seq_string_push(&str, 0xfffdU);
+ g_assert_false(rv);
+
+ buf = vte_seq_string_get(&str, &len);
+ for (unsigned int i = 0; i < len; i++)
+ g_assert_cmpuint(buf[i], ==, 0xfffdU);
+
+ vte_seq_string_reset(&str);
+ buf = vte_seq_string_get(&str, &len);
+ g_assert_cmpuint(len, ==, 0);
+
+ vte_seq_string_free(&str);
+}
+
+static void
test_seq_control(void)
{
static struct {
@@ -865,10 +985,10 @@ test_seq_csi_param(void)
}
static void
-test_seq_glue(char const* str,
- unsigned int n_args,
- unsigned int n_final_args,
- vte::parser::Sequence& seq)
+test_seq_glue_arg(char const* str,
+ unsigned int n_args,
+ unsigned int n_final_args,
+ vte::parser::Sequence& seq)
{
test_seq_parse(str, seq.seq_ptr());
@@ -888,11 +1008,11 @@ test_seq_glue(char const* str,
}
static void
-test_seq_glue(void)
+test_seq_glue_arg(void)
{
vte::parser::Sequence seq{};
- test_seq_glue(":0:1000;2;3;4;:;", 9, 6, seq);
+ test_seq_glue_arg(":0:1000;2;3;4;:;", 9, 6, seq);
g_assert_cmpuint(seq.cbegin(), ==, 0);
g_assert_cmpuint(seq.cend(), ==, 9);
@@ -992,6 +1112,227 @@ test_seq_glue(void)
g_assert_cmpint(seq.collect1(it, 42, 100, 200), ==, 100);
}
+static int
+feed_parser_st(vte_seq_builder& b,
+ struct vte_seq** seq,
+ bool c1 = false,
+ ssize_t max_arg_str_len = -1,
+ vte_seq_builder::VariantST st = vte_seq_builder::ST_DEFAULT)
+{
+ std::u32string s;
+ b.to_string(s, c1, max_arg_str_len, st);
+
+ auto rv = feed_parser(s, seq);
+ if (rv == VTE_SEQ_NONE)
+ return rv;
+
+ #if 0
+ switch (st) {
+ case ST_NONE:
+ g_assert_cmpuint(seq.terminator(), ==, 0);
+ break;
+ case ST_DEFAULT:
+ g_assert_cmpuint(seq.terminator(), ==, c1 ? 0x9C /* ST */ : 0x50 /* BACKSLASH */);
+ break;
+ case ST_C0:
+ g_assert_cmpuint(seq.terminator(), ==, 0x50 /* BACKSLASH */);
+ break;
+ case ST_C1:
+ g_assert_cmpuint(seq.terminator(), ==, 0x9C /* ST */);
+ break;
+ case ST_BEL:
+ g_assert_cmpuint(seq.terminator(), ==, 0x7 /* BEL */);
+ break;
+ }
+ #endif
+
+ return rv;
+}
+
+static void
+test_seq_osc(std::u32string const& str,
+ struct vte_seq** seq,
+ int expected_rv = VTE_SEQ_OSC,
+ bool c1 = true,
+ ssize_t max_arg_str_len = -1,
+ vte_seq_builder::VariantST st = vte_seq_builder::ST_DEFAULT)
+{
+ vte_seq_builder b{VTE_SEQ_OSC, str};
+
+ vte_parser_reset(parser);
+ auto rv = feed_parser_st(b, seq, c1, max_arg_str_len, st);
+ g_assert_cmpint(rv, ==, expected_rv);
+ #if 0
+ if (rv != VTE_SEQ_NONE)
+ b.assert_equal(*seq);
+ #endif
+}
+
+static void
+test_seq_osc(std::u32string const& str,
+ vte::parser::Sequence& seq,
+ int expected_rv = VTE_SEQ_OSC,
+ bool c1 = true,
+ ssize_t max_arg_str_len = -1,
+ vte_seq_builder::VariantST st = vte_seq_builder::ST_DEFAULT)
+{
+ test_seq_osc(str, seq.seq_ptr(), expected_rv, c1, max_arg_str_len, st);
+ if (expected_rv != VTE_SEQ_OSC)
+ return;
+
+ if (max_arg_str_len < 0 || size_t(max_arg_str_len) == str.size())
+ g_assert_true(seq.string() == str);
+ else
+ g_assert_true(seq.string() == str.substr(0, max_arg_str_len));
+}
+
+static void
+test_seq_osc(void)
+{
+ vte::parser::Sequence seq{};
+
+ /* Simple */
+ test_seq_osc(U""s, seq);
+ test_seq_osc(U"TEST"s, seq);
+
+ /* String of any supported length */
+ for (unsigned int len = 0; len < VTE_SEQ_STRING_MAX_CAPACITY; ++len)
+ test_seq_osc(std::u32string(len, 0x100000), seq);
+
+ /* Length exceeded */
+ // test_seq_osc(std::u32string(VTE_SEQ_STRING_MAX_CAPACITY + 1, 0x100000), seq, VTE_SEQ_NONE);
+
+ /* Test all introducer/ST combinations */
+ // test_seq_osc(U"TEST"s, seq, VTE_SEQ_NONE, false, -1, vte_seq_builder::ST_NONE);
+ // test_seq_osc(U"TEST"s, seq, VTE_SEQ_NONE, true, -1, vte_seq_builder::ST_NONE);
+ // test_seq_osc(U"TEST"s, seq, VTE_SEQ_OSC, false, -1, vte_seq_builder::ST_DEFAULT);
+ // test_seq_osc(U"TEST"s, seq, VTE_SEQ_OSC, true, -1, vte_seq_builder::ST_DEFAULT);
+ // test_seq_osc(U"TEST"s, seq, VTE_SEQ_OSC, false, -1, vte_seq_builder::ST_C0);
+ // test_seq_osc(U"TEST"s, seq, VTE_SEQ_OSC, true, -1, vte_seq_builder::ST_C0);
+ test_seq_osc(U"TEST"s, seq, VTE_SEQ_OSC, false, -1, vte_seq_builder::ST_C1);
+ test_seq_osc(U"TEST"s, seq, VTE_SEQ_OSC, true, -1, vte_seq_builder::ST_C1);
+ test_seq_osc(U"TEST"s, seq, VTE_SEQ_OSC, false, -1, vte_seq_builder::ST_BEL);
+ test_seq_osc(U"TEST"s, seq, VTE_SEQ_OSC, true, -1, vte_seq_builder::ST_BEL);
+}
+
+static void
+test_seq_glue_string(void)
+{
+ vte::parser::Sequence seq{};
+
+ std::u32string str{U"TEST"s};
+ test_seq_osc(str, seq);
+
+ g_assert_true(seq.string() == str);
+}
+
+static void
+test_seq_glue_string_tokeniser(void)
+{
+ std::string str{"a;1b:17:test::b:;3;5;def;17 a;ghi;"s};
+
+ vte::parser::StringTokeniser tokeniser{str, ';'};
+
+ auto start = tokeniser.cbegin();
+ auto end = tokeniser.cend();
+
+ auto pit = start;
+ for (auto it : {"a"s, "1b:17:test::b:"s, "3"s, "5"s, "def"s, "17 a"s, "ghi"s, ""s}) {
+ g_assert_true(it == *pit);
+
+ /* Use std::find to see if the InputIterator implementation
+ * is complete and correct.
+ */
+ auto fit = std::find(start, end, it);
+ g_assert_true(fit == pit);
+
+ ++pit;
+ }
+ g_assert_true(pit == end);
+
+ auto len = str.size();
+ size_t pos = 0;
+ pit = start;
+ for (auto it : {1, 14, 1, 1, 3, 4, 3, 0}) {
+ g_assert_cmpuint(it, ==, pit.size());
+ g_assert_cmpuint(len, ==, pit.size_remaining());
+
+ g_assert_true(pit.string_remaining() == str.substr(pos, std::string::npos));
+
+ len -= it + 1;
+ pos += it + 1;
+
+ ++pit;
+ }
+ g_assert_cmpuint(len + 1, ==, 0);
+ g_assert_cmpuint(pos, ==, str.size() + 1);
+
+ pit = start;
+ for (auto it : {-2, -2, 3, 5, -2, -2, -2, -1}) {
+ int num;
+ bool v = pit.number(num);
+ if (it == -2)
+ g_assert_false(v);
+ else
+ g_assert_cmpint(it, ==, num);
+
+ ++pit;
+ }
+
+ /* Test range for */
+ for (auto it : tokeniser)
+ ;
+
+ /* Test different separator */
+ pit = start;
+ ++pit;
+
+ auto substr = *pit;
+ vte::parser::StringTokeniser subtokeniser{substr, ':'};
+
+ auto subpit = subtokeniser.cbegin();
+ for (auto it : {"1b"s, "17"s, "test"s, ""s, "b"s, ""s}) {
+ g_assert_true(it == *subpit);
+
+ ++subpit;
+ }
+ g_assert_true(subpit == subtokeniser.cend());
+
+ /* Test another string, one that doesn't end with an empty token */
+ std::string str2{"abc;defghi"s};
+ vte::parser::StringTokeniser tokeniser2{str2, ';'};
+
+ g_assert_cmpint(std::distance(tokeniser2.cbegin(), tokeniser2.cend()), ==, 2);
+ auto pit2 = tokeniser2.cbegin();
+ g_assert_true(*pit2 == "abc"s);
+ ++pit2;
+ g_assert_true(*pit2 == "defghi"s);
+ ++pit2;
+ g_assert_true(pit2 == tokeniser2.cend());
+
+ /* Test another string, one that starts with an empty token */
+ std::string str3{";abc"s};
+ vte::parser::StringTokeniser tokeniser3{str3, ';'};
+
+ g_assert_cmpint(std::distance(tokeniser3.cbegin(), tokeniser3.cend()), ==, 2);
+ auto pit3 = tokeniser3.cbegin();
+ g_assert_true(*pit3 == ""s);
+ ++pit3;
+ g_assert_true(*pit3 == "abc"s);
+ ++pit3;
+ g_assert_true(pit3 == tokeniser3.cend());
+
+ /* And try an empty string, which should split into one empty token */
+ std::string str4{""s};
+ vte::parser::StringTokeniser tokeniser4{str4, ';'};
+
+ g_assert_cmpint(std::distance(tokeniser4.cbegin(), tokeniser4.cend()), ==, 1);
+ auto pit4 = tokeniser4.cbegin();
+ g_assert_true(*pit4 == ""s);
+ ++pit4;
+ g_assert_true(pit4 == tokeniser4.cend());
+}
+
int
main(int argc,
char* argv[])
@@ -1002,7 +1343,10 @@ main(int argc,
return 1;
g_test_add_func("/vte/parser/sequences/arg", test_seq_arg);
- g_test_add_func("/vte/parser/sequences/glue", test_seq_glue);
+ g_test_add_func("/vte/parser/sequences/string", test_seq_string);
+ g_test_add_func("/vte/parser/sequences/glue/arg", test_seq_glue_arg);
+ g_test_add_func("/vte/parser/sequences/glue/string", test_seq_glue_string);
+ g_test_add_func("/vte/parser/sequences/glue/string-tokeniser", test_seq_glue_string_tokeniser);
g_test_add_func("/vte/parser/sequences/control", test_seq_control);
g_test_add_func("/vte/parser/sequences/escape/invalid", test_seq_esc_invalid);
g_test_add_func("/vte/parser/sequences/escape/charset/94", test_seq_esc_charset_94);
@@ -1015,6 +1359,7 @@ main(int argc,
g_test_add_func("/vte/parser/sequences/escape/F[pes]", test_seq_esc_Fpes);
g_test_add_func("/vte/parser/sequences/csi", test_seq_csi);
g_test_add_func("/vte/parser/sequences/csi/parameters", test_seq_csi_param);
+ g_test_add_func("/vte/parser/sequences/osc", test_seq_osc);
auto rv = g_test_run();
diff --git a/src/parser.cc b/src/parser.cc
index 6ddc1c4..e7cb741 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -43,11 +43,8 @@
* detected sequences.
*/
-#define VTE_PARSER_ST_MAX (4096)
-
struct vte_parser {
struct vte_seq seq;
- size_t st_alloc;
unsigned int state;
};
@@ -876,7 +873,7 @@ enum parser_action {
ACTION_DCS_COLLECT, /* collect DCS data */
ACTION_DCS_CONSUME, /* consume DCS terminator */
ACTION_DCS_DISPATCH, /* dispatch DCS sequence */
- ACTION_OSC_START, /* start of OSC data */
+ ACTION_OSC_START, /* clear and clear string data */
ACTION_OSC_COLLECT, /* collect OSC data */
ACTION_OSC_CONSUME, /* consume OSC terminator */
ACTION_OSC_DISPATCH, /* dispatch OSC sequence */
@@ -897,12 +894,7 @@ int vte_parser_new(struct vte_parser **out)
if (!parser)
return -ENOMEM;
- parser->st_alloc = 64;
- parser->seq.st = (char*)kzalloc(parser->st_alloc + 1, GFP_KERNEL);
- if (!parser->seq.st) {
- kfree(parser);
- return -ENOMEM;
- }
+ vte_seq_string_init(&parser->seq.arg_str);
*out = parser;
return 0;
@@ -919,7 +911,7 @@ struct vte_parser *vte_parser_free(struct vte_parser *parser)
if (!parser)
return NULL;
- kfree(parser->seq.st);
+ vte_seq_string_free(&parser->seq.arg_str);
kfree(parser);
return NULL;
}
@@ -935,11 +927,12 @@ static inline void parser_clear(struct vte_parser *parser)
parser->seq.n_args = 0;
parser->seq.n_final_args = 0;
/* FIXME: we only really need to clear 0..n_args+1 since all others have not been touched */
+ // FIXMEchpe: now that DEFAULT is all-zero, use memset here
for (i = 0; i < VTE_PARSER_ARG_MAX; ++i)
parser->seq.args[i] = VTE_SEQ_ARG_INIT_DEFAULT;
- parser->seq.n_st = 0;
- parser->seq.st[0] = 0;
+ // FIXMEchpe not really needed here, just in parser_st_start
+ vte_seq_string_reset(&parser->seq.arg_str);
}
static int parser_ignore(struct vte_parser *parser, uint32_t raw)
@@ -1018,6 +1011,21 @@ static void parser_param(struct vte_parser *parser, uint32_t raw)
}
}
+static inline void parser_osc_start(struct vte_parser *parser)
+{
+ parser_clear(parser);
+}
+
+static void parser_collect_string(struct vte_parser *parser, uint32_t raw)
+{
+ /*
+ * Only characters from 0x20..0x7e and 0xa0..0x10ffff are allowed here.
+ * Our state-machine already verifies those restrictions.
+ */
+
+ vte_seq_string_push(&parser->seq.arg_str, raw);
+}
+
static int parser_esc(struct vte_parser *parser, uint32_t raw)
{
parser->seq.type = VTE_SEQ_ESCAPE;
@@ -1053,6 +1061,21 @@ static int parser_csi(struct vte_parser *parser, uint32_t raw)
return parser->seq.type;
}
+static int parser_osc(struct vte_parser *parser, uint32_t raw)
+{
+ /* parser->seq is cleared during OSC_START state, thus there's no need
+ * to clear invalid fields here. */
+
+ vte_seq_string_finish(&parser->seq.arg_str);
+
+ parser->seq.type = VTE_SEQ_OSC;
+ parser->seq.command = VTE_CMD_OSC;
+ parser->seq.terminator = raw;
+ parser->seq.charset = VTE_CHARSET_NONE;
+
+ return parser->seq.type;
+}
+
/* perform state transition and dispatch related actions */
static int parser_transition(struct vte_parser *parser,
uint32_t raw,
@@ -1097,17 +1120,16 @@ static int parser_transition(struct vte_parser *parser,
/* not implemented */
return VTE_SEQ_NONE;
case ACTION_OSC_START:
- /* not implemented */
+ parser_osc_start(parser);
return VTE_SEQ_NONE;
case ACTION_OSC_COLLECT:
- /* not implemented */
+ parser_collect_string(parser, raw);
return VTE_SEQ_NONE;
case ACTION_OSC_CONSUME:
/* not implemented */
return VTE_SEQ_NONE;
case ACTION_OSC_DISPATCH:
- /* not implemented */
- return VTE_SEQ_NONE;
+ return parser_osc(parser, raw);
default:
WARN(1, "invalid vte-parser action");
return VTE_SEQ_NONE;
@@ -1485,7 +1507,7 @@ int vte_parser_feed(struct vte_parser *parser,
break;
case 0x9d: /* OSC */
ret = parser_transition(parser, raw,
- STATE_OSC_STRING, ACTION_CLEAR);
+ STATE_OSC_STRING, ACTION_OSC_START);
break;
case 0x9b: /* CSI */
ret = parser_transition(parser, raw,
diff --git a/src/parser.hh b/src/parser.hh
index 1a723af..751ee11 100644
--- a/src/parser.hh
+++ b/src/parser.hh
@@ -21,6 +21,7 @@
#include <cstdio>
#include "parser-arg.hh"
+#include "parser-string.hh"
struct vte_parser;
struct vte_seq;
@@ -133,6 +134,14 @@ enum {
#undef _VTE_CHARSET_ALIAS
};
+enum {
+#define _VTE_OSC(osc,value) VTE_OSC_##osc = value,
+#include "parser-osc.hh"
+#undef _VTE_OSC
+
+ VTE_OSC_N
+};
+
struct vte_seq {
unsigned int type;
unsigned int command;
@@ -142,8 +151,7 @@ struct vte_seq {
unsigned int n_args;
unsigned int n_final_args;
vte_seq_arg_t args[VTE_PARSER_ARG_MAX];
- unsigned int n_st;
- char *st;
+ vte_seq_string_t arg_str;
};
int vte_parser_new(struct vte_parser **out);
diff --git a/src/vte.cc b/src/vte.cc
index cf1c55c..e312ae3 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -3693,9 +3693,11 @@ skip_chunk:
break;
}
+
case VTE_SEQ_NONE:
case VTE_SEQ_IGNORE:
break;
+
default: {
switch (seq.command()) {
#define _VTE_CMD(cmd) case VTE_CMD_##cmd: cmd(seq); break;
@@ -8536,21 +8538,11 @@ VteTerminalPrivate::~VteTerminalPrivate()
remove_update_timeout(this);
- /* discard title updates */
- g_free(m_window_title);
- g_free(m_window_title_changed);
- g_free(m_icon_title_changed);
- g_free(m_current_directory_uri_changed);
- g_free(m_current_directory_uri);
- g_free(m_current_file_uri_changed);
- g_free(m_current_file_uri);
-
/* Word char exceptions */
g_free(m_word_char_exceptions_string);
g_free(m_word_char_exceptions);
/* Free public-facing data. */
- g_free(m_icon_title);
if (m_vadjustment != NULL) {
/* Disconnect our signal handlers from this object. */
g_signal_handlers_disconnect_by_func(m_vadjustment,
@@ -10775,64 +10767,63 @@ VteTerminalPrivate::emit_pending_signals()
{
GObject *object = G_OBJECT(m_terminal);
g_object_freeze_notify(object);
- gboolean really_changed;
emit_adjustment_changed();
if (m_window_title_changed) {
- really_changed = (g_strcmp0(m_window_title, m_window_title_changed) != 0);
- g_free (m_window_title);
- m_window_title = m_window_title_changed;
- m_window_title_changed = NULL;
+ if (m_window_title != m_window_title_pending) {
+ m_window_title.swap(m_window_title_pending);
- if (really_changed) {
_vte_debug_print(VTE_DEBUG_SIGNALS,
"Emitting `window-title-changed'.\n");
g_signal_emit(object, signals[SIGNAL_WINDOW_TITLE_CHANGED], 0);
g_object_notify_by_pspec(object, pspecs[PROP_WINDOW_TITLE]);
}
+
+ m_window_title_pending.clear();
+ m_window_title_changed = false;
}
if (m_icon_title_changed) {
- really_changed = (g_strcmp0(m_icon_title, m_icon_title_changed) != 0);
- g_free (m_icon_title);
- m_icon_title = m_icon_title_changed;
- m_icon_title_changed = NULL;
+ if (m_icon_title != m_icon_title_pending) {
+ m_icon_title.swap(m_icon_title_pending);
- if (really_changed) {
_vte_debug_print(VTE_DEBUG_SIGNALS,
"Emitting `icon-title-changed'.\n");
g_signal_emit(object, signals[SIGNAL_ICON_TITLE_CHANGED], 0);
g_object_notify_by_pspec(object, pspecs[PROP_ICON_TITLE]);
}
+
+ m_icon_title_pending.clear();
+ m_icon_title_changed = false;
}
if (m_current_directory_uri_changed) {
- really_changed = (g_strcmp0(m_current_directory_uri, m_current_directory_uri_changed) != 0);
- g_free (m_current_directory_uri);
- m_current_directory_uri = m_current_directory_uri_changed;
- m_current_directory_uri_changed = NULL;
+ if (m_current_directory_uri != m_current_directory_uri_pending) {
+ m_current_directory_uri.swap(m_current_directory_uri_pending);
- if (really_changed) {
_vte_debug_print(VTE_DEBUG_SIGNALS,
"Emitting `current-directory-uri-changed'.\n");
g_signal_emit(object, signals[SIGNAL_CURRENT_DIRECTORY_URI_CHANGED], 0);
g_object_notify_by_pspec(object, pspecs[PROP_CURRENT_DIRECTORY_URI]);
}
+
+ m_current_directory_uri_pending.clear();
+ m_current_directory_uri_changed = false;
}
if (m_current_file_uri_changed) {
- really_changed = (g_strcmp0(m_current_file_uri, m_current_file_uri_changed) != 0);
- g_free (m_current_file_uri);
- m_current_file_uri = m_current_file_uri_changed;
- m_current_file_uri_changed = NULL;
+ if (m_current_file_uri != m_current_file_uri_pending) {
+ m_current_file_uri.swap(m_current_file_uri_pending);
- if (really_changed) {
_vte_debug_print(VTE_DEBUG_SIGNALS,
"Emitting `current-file-uri-changed'.\n");
g_signal_emit(object, signals[SIGNAL_CURRENT_FILE_URI_CHANGED], 0);
g_object_notify_by_pspec(object, pspecs[PROP_CURRENT_FILE_URI]);
}
+
+ m_current_file_uri_pending.clear();
+ m_current_file_uri_changed = false;
}
/* Flush any pending "inserted" signals. */
diff --git a/src/vtegtk.cc b/src/vtegtk.cc
index 516e042..57e259d 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -3456,7 +3456,8 @@ const char *
vte_terminal_get_current_directory_uri(VteTerminal *terminal)
{
g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
- return IMPL(terminal)->m_current_directory_uri;
+ auto impl = IMPL(terminal);
+ return impl->m_current_directory_uri.size() ? impl->m_current_directory_uri.data() : nullptr;
}
/**
@@ -3471,7 +3472,8 @@ const char *
vte_terminal_get_current_file_uri(VteTerminal *terminal)
{
g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
- return IMPL(terminal)->m_current_file_uri;
+ auto impl = IMPL(terminal);
+ return impl->m_current_file_uri.size() ? impl->m_current_file_uri.data() : nullptr;
}
/**
@@ -3886,7 +3888,7 @@ const char *
vte_terminal_get_icon_title(VteTerminal *terminal)
{
g_return_val_if_fail(VTE_IS_TERMINAL(terminal), "");
- return IMPL(terminal)->m_icon_title;
+ return IMPL(terminal)->m_icon_title.data();
}
/**
@@ -4172,7 +4174,7 @@ const char *
vte_terminal_get_window_title(VteTerminal *terminal)
{
g_return_val_if_fail(VTE_IS_TERMINAL(terminal), "");
- return IMPL(terminal)->m_window_title;
+ return IMPL(terminal)->m_window_title.data();
}
/**
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index bf38fbd..ea53321 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -493,15 +493,18 @@ public:
gboolean m_cursor_moved_pending;
gboolean m_contents_changed_pending;
- /* window name changes */
- char* m_window_title;
- char* m_window_title_changed;
- char* m_icon_title;
- char* m_icon_title_changed;
- char* m_current_directory_uri;
- char* m_current_directory_uri_changed;
- char* m_current_file_uri;
- char* m_current_file_uri_changed;
+ std::string m_window_title{};
+ std::string m_icon_title{};
+ std::string m_current_directory_uri{};
+ std::string m_current_file_uri{};
+ std::string m_window_title_pending{};
+ std::string m_icon_title_pending{};
+ std::string m_current_directory_uri_pending{};
+ std::string m_current_file_uri_pending{};
+ bool m_icon_title_changed{false};
+ bool m_window_title_changed{false};
+ bool m_current_directory_uri_changed{false};
+ bool m_current_file_uri_changed{false};
/* Background */
double m_background_alpha;
@@ -1132,10 +1135,6 @@ public:
GCancellable *cancellable,
GError **error);
- /* Sequence handlers and their helper functions */
- void handle_sequence(char const* match,
- vte::parser::Params const& params);
-
inline void ensure_cursor_is_onscreen();
inline void home_cursor();
inline void clear_screen();
@@ -1147,9 +1146,6 @@ public:
inline void switch_alternate_screen();
inline void save_cursor();
inline void restore_cursor();
- void set_title_internal(vte::parser::Params const& params,
- bool icon_title,
- bool window_title);
inline void set_mode_ecma(vte::parser::Sequence const& seq,
bool set) noexcept;
@@ -1191,19 +1187,11 @@ public:
inline void move_cursor_backward(vte::grid::column_t columns);
inline void move_cursor_forward(vte::grid::column_t columns);
inline void move_cursor_tab();
- inline void change_color(vte::parser::Params const& params,
- char const* terminator);
inline void line_feed();
- inline void set_current_hyperlink(char* hyperlink_params /* adopted */, char* uri /* adopted */);
inline void erase_in_display(vte::parser::Sequence const& seq);
inline void erase_in_line(vte::parser::Sequence const& seq);
inline void insert_lines(vte::grid::row_t param);
inline void delete_lines(vte::grid::row_t param);
- inline void change_special_color(vte::parser::Params const& params,
- int index,
- int index_fallback,
- int osc,
- char const *terminator);
void subscribe_accessible_events();
void select_text(vte::grid::column_t start_col,
@@ -1213,11 +1201,34 @@ public:
void select_empty(vte::grid::column_t col,
vte::grid::row_t row);
+ /* OSC handlers */
+ void set_color(vte::parser::Sequence const& seq,
+ vte::parser::StringTokeniser::const_iterator& token,
+ vte::parser::StringTokeniser::const_iterator const& endtoken) noexcept;
+ void set_special_color(vte::parser::Sequence const& seq,
+ vte::parser::StringTokeniser::const_iterator& token,
+ vte::parser::StringTokeniser::const_iterator const& endtoken,
+ int index,
+ int index_fallback,
+ int osc) noexcept;
+ void reset_color(vte::parser::Sequence const& seq,
+ vte::parser::StringTokeniser::const_iterator& token,
+ vte::parser::StringTokeniser::const_iterator const& endtoken) noexcept;
+ void set_current_directory_uri(vte::parser::Sequence const& seq,
+ vte::parser::StringTokeniser::const_iterator& token,
+ vte::parser::StringTokeniser::const_iterator const& endtoken)
noexcept;
+ void set_current_file_uri(vte::parser::Sequence const& seq,
+ vte::parser::StringTokeniser::const_iterator& token,
+ vte::parser::StringTokeniser::const_iterator const& endtoken) noexcept;
+ void set_current_hyperlink(vte::parser::Sequence const& seq,
+ vte::parser::StringTokeniser::const_iterator& token,
+ vte::parser::StringTokeniser::const_iterator const& endtoken) noexcept;
+
/* Sequence handlers */
/* old style */
-#define SEQUENCE_HANDLER(name) \
- inline void seq_ ## name (vte::parser::Params const& params);
+#define SEQUENCE_HANDLER(name) \
+ inline void seq_ ## name (vte::parser::Sequence const& seq);
#include "vteseq-list.hh"
#undef SEQUENCE_HANDLER
diff --git a/src/vteseq.cc b/src/vteseq.cc
index 692f560..8299974 100644
--- a/src/vteseq.cc
+++ b/src/vteseq.cc
@@ -33,7 +33,6 @@
#include <vte/vte.h>
#include "vteinternal.hh"
#include "vtegtk.hh"
-#include "vteutils.h" /* for strchrnul on non-GNU systems */
#include "caps.hh"
#include "debug.h"
@@ -42,6 +41,8 @@
#include <algorithm>
+using namespace std::literals;
+
void
vte::parser::Sequence::print() const
{
@@ -60,6 +61,11 @@ vte::parser::Sequence::print() const
}
g_printerr(" ]");
}
+ if (m_seq->type == VTE_SEQ_OSC) {
+ char* str = string_param();
+ g_printerr(" \"%s\"", str);
+ g_free(str);
+ }
g_printerr("\n");
#endif
}
@@ -102,6 +108,24 @@ vte::parser::Sequence::command_string() const
}
}
+// FIXMEchpe optimise this
+std::string
+vte::parser::Sequence::string_utf8() const noexcept
+{
+ std::string str;
+
+ size_t len;
+ auto buf = vte_seq_string_get(&m_seq->arg_str, &len);
+
+ char u[6];
+ for (size_t i = 0; i < len; ++i) {
+ auto ulen = g_unichar_to_utf8(buf[i], u);
+ str.append((char const*)u, ulen);
+ }
+
+ return str;
+}
+
/* A couple are duplicated from vte.c, to keep them static... */
/* Check how long a string of unichars is. Slow version. */
@@ -118,9 +142,11 @@ vte_unichar_strlen(gunichar const* c)
* length instead of walking the input twice.
*/
char*
-vte::parser::Sequence::ucs4_to_utf8(gunichar const* str) const
+vte::parser::Sequence::ucs4_to_utf8(gunichar const* str,
+ ssize_t len) const
{
- auto len = vte_unichar_strlen(str);
+ if (len < 0)
+ len = vte_unichar_strlen(str);
auto outlen = (len * VTE_UTF8_BPC) + 1;
auto result = (char*)g_try_malloc(outlen);
@@ -410,51 +436,6 @@ VteTerminalPrivate::switch_alternate_screen()
switch_screen(&m_alternate_screen);
}
-/* Set icon/window titles. */
-void
-VteTerminalPrivate::set_title_internal(vte::parser::Params const& params,
- bool change_icon_title,
- bool change_window_title)
-{
- if (change_icon_title == FALSE && change_window_title == FALSE)
- return;
-
- /* Get the string parameter's value. */
- char* title;
- if (!params.string_at(0, title))
- return;
-
- char *p, *validated;
- const char *end;
-
- //FIXMEchpe why? it's guaranteed UTF-8 already
- /* Validate the text. */
- g_utf8_validate(title, strlen(title), &end);
- validated = g_strndup(title, end - title);
-
- /* No control characters allowed. */
- for (p = validated; *p != '\0'; p++) {
- if ((*p & 0x1f) == *p) {
- *p = ' ';
- }
- }
-
- /* Emit the signal */
- if (change_window_title) {
- g_free(m_window_title_changed);
- m_window_title_changed = g_strdup(validated);
- }
-
- if (change_icon_title) {
- g_free(m_icon_title_changed);
- m_icon_title_changed = g_strdup(validated);
- }
-
- g_free (validated);
-
- g_free(title);
-}
-
void
VteTerminalPrivate::set_mode_ecma(vte::parser::Sequence const& seq,
bool set) noexcept
@@ -1020,90 +1001,6 @@ VteTerminalPrivate::move_cursor_forward(vte::grid::column_t columns)
}
}
-/* Internal helper for changing color in the palette */
-void
-VteTerminalPrivate::change_color(vte::parser::Params const& params,
- const char *terminator)
-{
- char **pairs;
- {
- char* str;
- if (!params.string_at(0, str))
- return;
-
- pairs = g_strsplit (str, ";", 0);
- g_free(str);
- }
-
- if (!pairs)
- return;
-
- vte::color::rgb color;
- guint idx, i;
-
- for (i = 0; pairs[i] && pairs[i + 1]; i += 2) {
- idx = strtoul (pairs[i], (char **) NULL, 10);
-
- if (idx >= VTE_DEFAULT_FG)
- continue;
-
- if (color.parse(pairs[i + 1])) {
- set_color(idx, VTE_COLOR_SOURCE_ESCAPE, color);
- } else if (strcmp (pairs[i + 1], "?") == 0) {
- gchar buf[128];
- auto c = get_color(idx);
- g_assert(c != NULL);
- g_snprintf (buf, sizeof (buf),
- _VTE_CAP_OSC "4;%u;rgb:%04x/%04x/%04x%s",
- idx, c->red, c->green, c->blue, terminator);
- feed_child(buf, -1);
- }
- }
-
- g_strfreev (pairs);
-
- /* emit the refresh as the palette has changed and previous
- * renders need to be updated. */
- emit_refresh_window();
-}
-
-/* Change color in the palette, BEL terminated */
-void
-VteTerminalPrivate::seq_change_color_bel(vte::parser::Params const& params)
-{
- change_color(params, BEL_C0);
-}
-
-/* Change color in the palette, ST_C0 terminated */
-void
-VteTerminalPrivate::seq_change_color_st(vte::parser::Params const& params)
-{
- change_color(params, ST_C0);
-}
-
-/* Reset color in the palette */
-void
-VteTerminalPrivate::seq_reset_color(vte::parser::Params const& params)
-{
- auto n_params = params.size();
- if (n_params) {
- for (unsigned int i = 0; i < n_params; i++) {
- int value;
- if (!params.number_at_unchecked(i, value))
- continue;
-
- if (value < 0 || value >= VTE_DEFAULT_FG)
- continue;
-
- reset_color(value, VTE_COLOR_SOURCE_ESCAPE);
- }
- } else {
- for (unsigned int idx = 0; idx < VTE_DEFAULT_FG; idx++) {
- reset_color(idx, VTE_COLOR_SOURCE_ESCAPE);
- }
- }
-}
-
void
VteTerminalPrivate::line_feed()
{
@@ -1305,161 +1202,6 @@ VteTerminalPrivate::seq_parse_sgr_color(vte::parser::Sequence const& seq,
return false;
}
-/* Set one or the other. */
-void
-VteTerminalPrivate::seq_set_icon_title(vte::parser::Params const& params)
-{
- set_title_internal(params, true, false);
-}
-
-void
-VteTerminalPrivate::seq_set_window_title(vte::parser::Params const& params)
-{
- set_title_internal(params, false, true);
-}
-
-/* Set both the window and icon titles to the same string. */
-void
-VteTerminalPrivate::seq_set_icon_and_window_title(vte::parser::Params const& params)
-{
- set_title_internal(params, true, true);
-}
-
-void
-VteTerminalPrivate::seq_set_current_directory_uri(vte::parser::Params const& params)
-{
- char* uri = nullptr;
- if (params.string_at(0, uri)) {
- /* Validate URI */
- if (uri[0]) {
- auto filename = g_filename_from_uri (uri, nullptr, nullptr);
- if (filename == nullptr) {
- /* invalid URI */
- g_free (uri);
- uri = nullptr;
- } else {
- g_free (filename);
- }
- } else {
- g_free(uri);
- uri = nullptr;
- }
- }
-
- g_free(m_current_directory_uri_changed);
- m_current_directory_uri_changed = uri /* adopt */;
-}
-
-void
-VteTerminalPrivate::seq_set_current_file_uri(vte::parser::Params const& params)
-{
- char* uri = nullptr;
- if (params.string_at(0, uri)) {
- /* Validate URI */
- if (uri[0]) {
- auto filename = g_filename_from_uri (uri, nullptr, nullptr);
- if (filename == nullptr) {
- /* invalid URI */
- g_free (uri);
- uri = nullptr;
- } else {
- g_free (filename);
- }
- } else {
- g_free(uri);
- uri = nullptr;
- }
- }
-
- g_free(m_current_file_uri_changed);
- m_current_file_uri_changed = uri /* adopt */;
-}
-
-/* Handle OSC 8 hyperlinks.
- * See bug 779734 and https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda. */
-void
-VteTerminalPrivate::seq_set_current_hyperlink(vte::parser::Params const& params)
-{
-
- char* hyperlink_params = nullptr;
- char* uri = nullptr;
- if (params.size() >= 2) {
- params.string_at_unchecked(0, hyperlink_params);
- params.string_at_unchecked(1, uri);
- }
-
- set_current_hyperlink(hyperlink_params, uri);
-}
-
-void
-VteTerminalPrivate::set_current_hyperlink(char *hyperlink_params /* adopted */,
- char* uri /* adopted */)
-{
- guint idx;
- char *id = NULL;
- char idbuf[24];
-
- if (!m_allow_hyperlink)
- return;
-
- /* Get the "id" parameter */
- if (hyperlink_params) {
- if (strncmp(hyperlink_params, "id=", 3) == 0) {
- id = hyperlink_params + 3;
- } else {
- id = strstr(hyperlink_params, ":id=");
- if (id)
- id += 4;
- }
- }
- if (id) {
- *strchrnul(id, ':') = '\0';
- }
- _vte_debug_print (VTE_DEBUG_HYPERLINK,
- "OSC 8: id=\"%s\" uri=\"%s\"\n",
- id, uri);
-
- if (uri && strlen(uri) > VTE_HYPERLINK_URI_LENGTH_MAX) {
- _vte_debug_print (VTE_DEBUG_HYPERLINK,
- "Overlong URI ignored: \"%s\"\n",
- uri);
- uri[0] = '\0';
- }
-
- if (id && strlen(id) > VTE_HYPERLINK_ID_LENGTH_MAX) {
- _vte_debug_print (VTE_DEBUG_HYPERLINK,
- "Overlong \"id\" ignored: \"%s\"\n",
- id);
- id[0] = '\0';
- }
-
- if (uri && uri[0]) {
- /* The hyperlink, as we carry around and store in the streams, is "id;uri" */
- char *hyperlink;
-
- if (!id || !id[0]) {
- /* Automatically generate a unique ID string. The colon makes sure
- * it cannot conflict with an explicitly specified one. */
- sprintf(idbuf, ":%ld", m_hyperlink_auto_id++);
- id = idbuf;
- _vte_debug_print (VTE_DEBUG_HYPERLINK,
- "Autogenerated id=\"%s\"\n",
- id);
- }
- hyperlink = g_strdup_printf("%s;%s", id, uri);
- idx = _vte_ring_get_hyperlink_idx(m_screen->row_data, hyperlink);
- g_free (hyperlink);
- } else {
- /* idx = 0; also remove the previous current_idx so that it can be GC'd now. */
- idx = _vte_ring_get_hyperlink_idx(m_screen->row_data, NULL);
- }
-
- m_defaults.attr.hyperlink_idx = idx;
-
- g_free(hyperlink_params);
- g_free(uri);
-}
-
void
VteTerminalPrivate::erase_in_display(vte::parser::Sequence const& seq)
{
@@ -1467,7 +1209,7 @@ VteTerminalPrivate::erase_in_display(vte::parser::Sequence const& seq)
* bool selective = (seq.command() == VTE_CMD_DECSED);
*/
- switch (seq[0]) {
+ switch (seq.collect1(0)) {
case -1: /* default */
case 0:
/* Clear below the current line. */
@@ -1502,7 +1244,7 @@ VteTerminalPrivate::erase_in_line(vte::parser::Sequence const& seq)
* bool selective = (seq.command() == VTE_CMD_DECSEL);
*/
- switch (seq[0]) {
+ switch (seq.collect1(0)) {
case -1: /* default */
case 0:
/* Clear to end of the line. */
@@ -1592,166 +1334,244 @@ VteTerminalPrivate::delete_lines(vte::grid::row_t param)
m_text_deleted_flag = TRUE;
}
-/* Internal helper for setting/querying special colors */
void
-VteTerminalPrivate::change_special_color(vte::parser::Params const& params,
- int index,
- int index_fallback,
- int osc,
- const char *terminator)
+VteTerminalPrivate::set_color(vte::parser::Sequence const& seq,
+ vte::parser::StringTokeniser::const_iterator& token,
+ vte::parser::StringTokeniser::const_iterator const& endtoken) noexcept
{
- char* name;
- if (!params.string_at(0, name))
- return;
+ bool any_changed = false;
- vte::color::rgb color;
-
- if (color.parse(name))
- set_color(index, VTE_COLOR_SOURCE_ESCAPE, color);
- else if (strcmp (name, "?") == 0) {
- gchar buf[128];
- auto c = get_color(index);
- if (c == NULL && index_fallback != -1)
- c = get_color(index_fallback);
- g_assert(c != NULL);
- g_snprintf (buf, sizeof (buf),
- _VTE_CAP_OSC "%d;rgb:%04x/%04x/%04x%s",
- osc, c->red, c->green, c->blue, terminator);
- feed_child(buf, -1);
- }
-}
+ while (token != endtoken) {
+ int value;
+ bool has_value = token.number(value);
-/* Change the default foreground cursor, BEL terminated */
-void
-VteTerminalPrivate::seq_change_foreground_color_bel(vte::parser::Params const& params)
-{
- change_special_color(params, VTE_DEFAULT_FG, -1, 10, BEL_C0);
-}
+ if (++token == endtoken)
+ break;
-/* Change the default foreground cursor, ST_C0 terminated */
-void
-VteTerminalPrivate::seq_change_foreground_color_st(vte::parser::Params const& params)
-{
- change_special_color(params, VTE_DEFAULT_FG, -1, 10, ST_C0);
-}
+ if (!has_value ||
+ value < 0 ||
+ value >= VTE_DEFAULT_FG) {
+ ++token;
+ continue;
+ }
-/* Reset the default foreground color */
-void
-VteTerminalPrivate::seq_reset_foreground_color(vte::parser::Params const& params)
-{
- reset_color(VTE_DEFAULT_FG, VTE_COLOR_SOURCE_ESCAPE);
-}
+ auto const str = *token;
+
+ if (str == "?"s) {
+ gchar buf[128];
+ auto c = get_color(value);
+ g_assert_nonnull(c);
+ g_snprintf (buf, sizeof (buf),
+ _VTE_CAP_OSC "4;%u;rgb:%04x/%04x/%04x%s",
+ value, c->red, c->green, c->blue,
+ seq.terminator() == 0x7 ? BEL_C0 : ST_C0);
+ feed_child(buf, -1);
+ } else {
+ vte::color::rgb color;
+ if (color.parse(str.data())) {
+ set_color(value, VTE_COLOR_SOURCE_ESCAPE, color);
+ any_changed = true;
+ }
+ }
-/* Change the default background cursor, BEL terminated */
-void
-VteTerminalPrivate::seq_change_background_color_bel(vte::parser::Params const& params)
-{
- change_special_color(params, VTE_DEFAULT_BG, -1, 11, BEL_C0);
-}
+ ++token;
+ }
-/* Change the default background cursor, ST_C0 terminated */
-void
-VteTerminalPrivate::seq_change_background_color_st(vte::parser::Params const& params)
-{
- change_special_color(params, VTE_DEFAULT_BG, -1, 11, ST_C0);
+ /* emit the refresh as the palette has changed and previous
+ * renders need to be updated. */
+ if (any_changed)
+ emit_refresh_window();
}
-/* Reset the default background color */
void
-VteTerminalPrivate::seq_reset_background_color(vte::parser::Params const& params)
+VteTerminalPrivate::set_special_color(vte::parser::Sequence const& seq,
+ vte::parser::StringTokeniser::const_iterator& token,
+ vte::parser::StringTokeniser::const_iterator const& endtoken,
+ int index,
+ int index_fallback,
+ int osc)
{
- reset_color(VTE_DEFAULT_BG, VTE_COLOR_SOURCE_ESCAPE);
-}
+ if (token == endtoken)
+ return;
-/* Change the color of the cursor background, BEL terminated */
-void
-VteTerminalPrivate::seq_change_cursor_background_color_bel(vte::parser::Params const& params)
-{
- change_special_color(params, VTE_CURSOR_BG, VTE_DEFAULT_FG, 12, BEL_C0);
-}
+ auto const str = *token;
+ if (str == "?"s) {
+ gchar buf[128];
+ auto c = get_color(index);
+ if (c == nullptr && index_fallback != -1)
+ c = get_color(index_fallback);
+ g_assert_nonnull(c);
+ auto len = g_snprintf (buf, sizeof (buf),
+ _VTE_CAP_OSC "%d;rgb:%04x/%04x/%04x%s",
+ osc,
+ c->red, c->green, c->blue,
+ ST_C0);//seq.terminator() == 7 ? BEL_C0 : ST_C0);
+ feed_child(buf, len);
+ } else {
+ vte::color::rgb color;
+ if (color.parse(str.data())) {
+ set_color(index, VTE_COLOR_SOURCE_ESCAPE, color);
-/* Change the color of the cursor background, ST_C0 terminated */
-void
-VteTerminalPrivate::seq_change_cursor_background_color_st(vte::parser::Params const& params)
-{
- change_special_color(params, VTE_CURSOR_BG, VTE_DEFAULT_FG, 12, ST_C0);
+ /* emit the refresh as the palette has changed and previous
+ * renders need to be updated. */
+ emit_refresh_window();
+ }
+ }
}
-/* Reset the color of the cursor */
void
-VteTerminalPrivate::seq_reset_cursor_background_color(vte::parser::Params const& params)
+VteTerminalPrivate::reset_color(vte::parser::Sequence const& seq,
+ vte::parser::StringTokeniser::const_iterator& token,
+ vte::parser::StringTokeniser::const_iterator const& endtoken) noexcept
{
- reset_color(VTE_CURSOR_BG, VTE_COLOR_SOURCE_ESCAPE);
-}
+ /* Empty param? Reset all */
+ if (token == endtoken ||
+ token.size_remaining() == 0) {
+ for (unsigned int idx = 0; idx < VTE_DEFAULT_FG; idx++)
+ reset_color(idx, VTE_COLOR_SOURCE_ESCAPE);
-/* Change the highlight background color, BEL terminated */
-void
-VteTerminalPrivate::seq_change_highlight_background_color_bel(vte::parser::Params const& params)
-{
- change_special_color(params, VTE_HIGHLIGHT_BG, VTE_DEFAULT_FG, 17, BEL_C0);
-}
+ /* emit the refresh as the palette has changed and previous
+ * renders need to be updated. */
+ emit_refresh_window();
+ return;
+ }
-/* Change the highlight background color, ST_C0 terminated */
-void
-VteTerminalPrivate::seq_change_highlight_background_color_st(vte::parser::Params const& params)
-{
- change_special_color(params, VTE_HIGHLIGHT_BG, VTE_DEFAULT_FG, 17, ST_C0);
-}
+ bool any_changed = false;
-/* Reset the highlight background color */
-void
-VteTerminalPrivate::seq_reset_highlight_background_color(vte::parser::Params const& params)
-{
- reset_color(VTE_HIGHLIGHT_BG, VTE_COLOR_SOURCE_ESCAPE);
-}
+ while (token != endtoken) {
+ int value;
+ if (!token.number(value))
+ continue;
-/* Change the highlight foreground color, BEL terminated */
-void
-VteTerminalPrivate::seq_change_highlight_foreground_color_bel(vte::parser::Params const& params)
-{
- change_special_color(params, VTE_HIGHLIGHT_FG, VTE_DEFAULT_BG, 19, BEL_C0);
-}
+ if (0 <= value && value < VTE_DEFAULT_FG) {
+ reset_color(value, VTE_COLOR_SOURCE_ESCAPE);
+ any_changed = true;
+ }
-/* Change the highlight foreground color, ST_C0 terminated */
-void
-VteTerminalPrivate::seq_change_highlight_foreground_color_st(vte::parser::Params const& params)
-{
- change_special_color(params, VTE_HIGHLIGHT_FG, VTE_DEFAULT_BG, 19, ST_C0);
+ ++token;
+ }
+
+ /* emit the refresh as the palette has changed and previous
+ * renders need to be updated. */
+ if (any_changed)
+ emit_refresh_window();
}
-/* Reset the highlight foreground color */
void
-VteTerminalPrivate::seq_reset_highlight_foreground_color(vte::parser::Params const& params)
+VteTerminalPrivate::set_current_directory_uri(vte::parser::Sequence const& seq,
+ vte::parser::StringTokeniser::const_iterator& token,
+ vte::parser::StringTokeniser::const_iterator const& endtoken)
noexcept
{
- reset_color(VTE_HIGHLIGHT_FG, VTE_COLOR_SOURCE_ESCAPE);
-}
+ std::string uri;
+ if (token != endtoken && token.size_remaining() > 0) {
+ uri = token.string_remaining();
-/* URXVT generic OSC 777 */
+ auto filename = g_filename_from_uri(uri.data(), nullptr, nullptr);
+ if (filename != nullptr) {
+ g_free(filename);
+ } else {
+ /* invalid URI */
+ uri.clear();
+ }
+ }
-void
-VteTerminalPrivate::seq_urxvt_777(vte::parser::Params const& params)
-{
- /* Accept but ignore this for compatibility with downstream-patched vte (bug #711059)*/
+ m_current_directory_uri_pending.swap(uri);
+ m_current_directory_uri_changed = true;
}
-/* iterm2 OSC 133 & 1337 */
-
void
-VteTerminalPrivate::seq_iterm2_133(vte::parser::Params const& params)
+VteTerminalPrivate::set_current_file_uri(vte::parser::Sequence const& seq,
+ vte::parser::StringTokeniser::const_iterator& token,
+ vte::parser::StringTokeniser::const_iterator const& endtoken)
noexcept
+
{
- /* Accept but ignore this for compatibility when sshing to an osx host
- * where the iterm2 integration is loaded even when not actually using
- * iterm2.
- */
+ std::string uri;
+ if (token != endtoken && token.size_remaining() > 0) {
+ uri = token.string_remaining();
+
+ auto filename = g_filename_from_uri(uri.data(), nullptr, nullptr);
+ if (filename != nullptr) {
+ g_free(filename);
+ } else {
+ /* invalid URI */
+ uri.clear();
+ }
+ }
+
+ m_current_file_uri_pending.swap(uri);
+ m_current_file_uri_changed = true;
}
void
-VteTerminalPrivate::seq_iterm2_1337(vte::parser::Params const& params)
+VteTerminalPrivate::set_current_hyperlink(vte::parser::Sequence const& seq,
+ vte::parser::StringTokeniser::const_iterator& token,
+ vte::parser::StringTokeniser::const_iterator const& endtoken)
noexcept
{
- /* Accept but ignore this for compatibility when sshing to an osx host
- * where the iterm2 integration is loaded even when not actually using
- * iterm2.
+ if (token == endtoken)
+ return; // FIXMEchpe or should we treat this as a reset?
+
+ /* Handle OSC 8 hyperlinks.
+ * See bug 779734 and https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
*/
+
+ if (!m_allow_hyperlink)
+ return;
+
+ /* The hyperlink, as we carry around and store in the streams, is "id;uri" */
+ std::string hyperlink;
+
+ /* First, find the ID */
+ vte::parser::StringTokeniser subtokeniser{*token, ':'};
+ for (auto subtoken : subtokeniser) {
+ auto const len = subtoken.size();
+ if (len < 3)
+ continue;
+
+ if (subtoken[0] != 'i' || subtoken[1] != 'd' || subtoken[2] != '=')
+ continue;
+
+ if (len > 3 + VTE_HYPERLINK_ID_LENGTH_MAX) {
+ _vte_debug_print (VTE_DEBUG_HYPERLINK, "Overlong \"id\" ignored: \"%s\"\n",
+ subtoken.data());
+ break;
+ }
+
+ hyperlink = subtoken.substr(3);
+ break;
+ }
+
+ if (hyperlink.size() == 0) {
+ /* Automatically generate a unique ID string. The colon makes sure
+ * it cannot conflict with an explicitly specified one.
+ */
+ char idbuf[24];
+ auto len = g_snprintf(idbuf, sizeof(idbuf), ":%ld", m_hyperlink_auto_id++);
+ hyperlink.append(idbuf, len);
+ _vte_debug_print (VTE_DEBUG_HYPERLINK, "Autogenerated id=\"%s\"\n", hyperlink.data());
+ }
+
+ /* Now get the URI */
+ if (++token == endtoken)
+ return; // FIXMEchpe or should we treat this the same as 0-length URI ?
+
+ hyperlink.push_back(';');
+ guint idx;
+ auto const len = token.size_remaining();
+ if (len > 0 && len <= VTE_HYPERLINK_URI_LENGTH_MAX) {
+ token.append_remaining(hyperlink);
+
+ _vte_debug_print (VTE_DEBUG_HYPERLINK, "OSC 8: id;uri=\"%s\"\n", hyperlink.data());
+
+ idx = _vte_ring_get_hyperlink_idx(m_screen->row_data, hyperlink.data());
+ } else {
+ if (G_UNLIKELY(len > VTE_HYPERLINK_URI_LENGTH_MAX))
+ _vte_debug_print (VTE_DEBUG_HYPERLINK, "Overlong URI ignored (len %" G_GSIZE_FORMAT
")\n", len);
+
+ /* idx = 0; also remove the previous current_idx so that it can be GC'd now. */
+ idx = _vte_ring_get_hyperlink_idx(m_screen->row_data, nullptr);
+ }
+
+ m_defaults.attr.hyperlink_idx = idx;
}
/*
@@ -4353,6 +4173,172 @@ VteTerminalPrivate::NUL(vte::parser::Sequence const& seq)
}
void
+VteTerminalPrivate::OSC(vte::parser::Sequence const& seq)
+{
+ /*
+ * OSC - operating system command
+ *
+ * References: ECMA-48 § 8.3.89
+ * XTERM
+ */
+
+ /* Our OSC have the format
+ * OSC number ; rest of string ST
+ * where the rest of the string may or may not contain more semicolons.
+ *
+ * First, extract the number.
+ */
+
+ auto const str = seq.string_utf8();
+ vte::parser::StringTokeniser tokeniser{str, ';'};
+ auto it = tokeniser.cbegin();
+ int osc;
+ if (!it.number(osc))
+ return;
+
+ auto const cend = tokeniser.cend();
+ ++it; /* could now be cend */
+
+ switch (osc) {
+ case VTE_OSC_VTECWF:
+ set_current_file_uri(seq, it, cend);
+ break;
+
+ case VTE_OSC_VTECWD:
+ set_current_directory_uri(seq, it, cend);
+ break;
+
+ case VTE_OSC_VTEHYPER:
+ set_current_hyperlink(seq, it, cend);
+ break;
+
+ case -1: /* default */
+ case VTE_OSC_XTERM_SET_WINDOW_AND_ICON_TITLE: {
+ std::string title;
+ if (it != cend)
+ title = it.string_remaining();
+ m_icon_title_pending = title;
+ m_window_title_pending.swap(title);
+ m_icon_title_changed = true;
+ m_window_title_changed = true;
+ break;
+ }
+
+ case VTE_OSC_XTERM_SET_ICON_TITLE: {
+ std::string title;
+ if (it != cend)
+ title = it.string_remaining();
+ m_icon_title_pending.swap(title);
+ m_icon_title_changed = true;
+ break;
+ }
+
+ case VTE_OSC_XTERM_SET_WINDOW_TITLE: {
+ std::string title;
+ if (it != cend)
+ title = it.string_remaining();
+ m_window_title_pending.swap(title);
+ m_window_title_changed = true;
+ break;
+ }
+
+ case VTE_OSC_XTERM_SET_COLOR:
+ set_color(seq, it, cend);
+ break;
+
+ case VTE_OSC_XTERM_SET_COLOR_TEXT_FG:
+ set_special_color(seq, it, cend, VTE_DEFAULT_FG, -1, osc);
+ break;
+
+ case VTE_OSC_XTERM_SET_COLOR_TEXT_BG:
+ set_special_color(seq, it, cend, VTE_DEFAULT_BG, -1, osc);
+ break;
+
+ case VTE_OSC_XTERM_SET_COLOR_CURSOR_BG:
+ set_special_color(seq, it, cend, VTE_CURSOR_BG, VTE_DEFAULT_FG, osc);
+ break;
+
+ case VTE_OSC_XTERM_SET_COLOR_HIGHLIGHT_BG:
+ set_special_color(seq, it, cend, VTE_HIGHLIGHT_BG, VTE_DEFAULT_FG, osc);
+ break;
+
+ case VTE_OSC_XTERM_SET_COLOR_HIGHLIGHT_FG:
+ set_special_color(seq, it, cend, VTE_HIGHLIGHT_FG, VTE_DEFAULT_BG, osc);
+ break;
+
+ case VTE_OSC_XTERM_RESET_COLOR:
+ reset_color(seq, it, cend);
+ break;
+
+ case VTE_OSC_XTERM_RESET_COLOR_TEXT_FG:
+ reset_color(VTE_DEFAULT_FG, VTE_COLOR_SOURCE_ESCAPE);
+ break;
+
+ case VTE_OSC_XTERM_RESET_COLOR_TEXT_BG:
+ reset_color(VTE_DEFAULT_BG, VTE_COLOR_SOURCE_ESCAPE);
+ break;
+
+ case VTE_OSC_XTERM_RESET_COLOR_CURSOR_BG:
+ reset_color(VTE_CURSOR_BG, VTE_COLOR_SOURCE_ESCAPE);
+ break;
+
+ case VTE_OSC_XTERM_RESET_COLOR_HIGHLIGHT_BG:
+ reset_color(VTE_HIGHLIGHT_BG, VTE_COLOR_SOURCE_ESCAPE);
+ break;
+
+ case VTE_OSC_XTERM_RESET_COLOR_HIGHLIGHT_FG:
+ reset_color(VTE_HIGHLIGHT_FG, VTE_COLOR_SOURCE_ESCAPE);
+ break;
+
+ case VTE_OSC_XTERM_SET_XPROPERTY:
+ case VTE_OSC_XTERM_SET_COLOR_SPECIAL:
+ case VTE_OSC_XTERM_SET_COLOR_MOUSE_CURSOR_FG:
+ case VTE_OSC_XTERM_SET_COLOR_MOUSE_CURSOR_BG:
+ case VTE_OSC_XTERM_SET_COLOR_TEK_FG:
+ case VTE_OSC_XTERM_SET_COLOR_TEK_BG:
+ case VTE_OSC_XTERM_SET_COLOR_TEK_CURSOR:
+ case VTE_OSC_XTERM_LOGFILE:
+ case VTE_OSC_XTERM_SET_FONT:
+ case VTE_OSC_XTERM_SET_XSELECTION:
+ case VTE_OSC_XTERM_RESET_COLOR_SPECIAL:
+ case VTE_OSC_XTERM_SET_COLOR_MODE:
+ case VTE_OSC_XTERM_RESET_COLOR_MOUSE_CURSOR_FG:
+ case VTE_OSC_XTERM_RESET_COLOR_MOUSE_CURSOR_BG:
+ case VTE_OSC_XTERM_RESET_COLOR_TEK_FG:
+ case VTE_OSC_XTERM_RESET_COLOR_TEK_BG:
+ case VTE_OSC_XTERM_RESET_COLOR_TEK_CURSOR:
+ case VTE_OSC_EMACS_51:
+ case VTE_OSC_ITERM2_133:
+ case VTE_OSC_ITERM2_1337:
+ case VTE_OSC_ITERM2_GROWL:
+ case VTE_OSC_KONSOLE_30:
+ case VTE_OSC_KONSOLE_31:
+ case VTE_OSC_RLOGIN_SET_KANJI_MODE:
+ case VTE_OSC_RLOGIN_SPEECH:
+ case VTE_OSC_RXVT_SET_BACKGROUND_PIXMAP:
+ case VTE_OSC_RXVT_SET_COLOR_FG:
+ case VTE_OSC_RXVT_SET_COLOR_BG:
+ case VTE_OSC_RXVT_DUMP_SCREEN:
+ case VTE_OSC_URXVT_SET_LOCALE:
+ case VTE_OSC_URXVT_VERSION:
+ case VTE_OSC_URXVT_SET_COLOR_TEXT_ITALIC:
+ case VTE_OSC_URXVT_SET_COLOR_TEXT_BOLD:
+ case VTE_OSC_URXVT_SET_COLOR_UNDERLINE:
+ case VTE_OSC_URXVT_SET_COLOR_BORDER:
+ case VTE_OSC_URXVT_SET_FONT:
+ case VTE_OSC_URXVT_SET_FONT_BOLD:
+ case VTE_OSC_URXVT_SET_FONT_ITALIC:
+ 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;
+ }
+}
+
+void
VteTerminalPrivate::PP(vte::parser::Sequence const& seq)
{
/*
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]