[vte] lib: Replace SIXEL handling
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vte] lib: Replace SIXEL handling
- Date: Sun, 18 Oct 2020 22:17:08 +0000 (UTC)
commit e0ff2c71b71f45c50d8d704f411f2d2f5c18e469
Author: Christian Persch <chpe src gnome org>
Date: Mon Oct 19 00:16:36 2020 +0200
lib: Replace SIXEL handling
Remove the old sixel parser and context, and move over to the new parser
and context.
https://gitlab.gnome.org/GNOME/vte/-/issues/253
src/icu-decoder.hh | 15 ++
src/image.hh | 2 +-
src/meson.build | 2 -
src/parser-seq.py | 2 +-
src/parser-string.hh | 9 -
src/ring.cc | 16 +-
src/ring.hh | 5 +-
src/sixelparser.cc | 713 ---------------------------------------------------
src/sixelparser.hh | 75 ------
src/vte.cc | 149 +++++++++--
src/vteinternal.hh | 35 ++-
src/vteseq.cc | 207 ++++++++-------
12 files changed, 307 insertions(+), 923 deletions(-)
---
diff --git a/src/icu-decoder.hh b/src/icu-decoder.hh
index 8c4eada1..094e9400 100644
--- a/src/icu-decoder.hh
+++ b/src/icu-decoder.hh
@@ -68,6 +68,21 @@ public:
void reset() noexcept;
+ /*
+ * pending():
+ *
+ * Returns whether the converter has output pending (i.e. will produce more
+ * output without consuming more input.
+ */
+ constexpr auto pending() const noexcept
+ {
+ /* Due to the way we drive the ICU converter and the way ICU converters work
+ * by first writing out any internally buffered output before consuming more
+ * input, this should be a safe guess about whether there is pending output.
+ */
+ return m_index + 1 < m_available;
+ }
+
private:
enum class State {
eInput = 0,
diff --git a/src/image.hh b/src/image.hh
index fa972c9b..9537bbca 100644
--- a/src/image.hh
+++ b/src/image.hh
@@ -46,7 +46,7 @@ private:
int m_cell_height;
public:
- Image(vte::cairo::Surface&& surface,
+ Image(vte::cairo::Surface surface,
int priority,
int width_pixels,
int height_pixels,
diff --git a/src/meson.build b/src/meson.build
index 1b6e8b27..248053c9 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -111,8 +111,6 @@ sixel_context_sources = files(
sixel_sources = sixel_parser_sources + sixel_context_sources + files(
'image.cc',
'image.hh',
- 'sixelparser.cc',
- 'sixelparser.hh',
)
systemd_sources = files(
diff --git a/src/parser-seq.py b/src/parser-seq.py
index 7b2c6ebe..90a68246 100755
--- a/src/parser-seq.py
+++ b/src/parser-seq.py
@@ -865,7 +865,7 @@ sequences = [
comment='restore terminal state'),
seq_DCS('XTERM_STCAP', 'p', intermediates=(Intermediate.PLUS,), flags=Flags.NOP,
comment='xterm set termcap/terminfo'),
- seq_DCS('DECSIXEL', 'q',
+ seq_DCS('DECSIXEL', 'q', flags=Flags.UNRIPE | Flags.HANDLER_RV,
comment='SIXEL graphics'),
seq_DCS('DECRQSS', 'q', intermediates=(Intermediate.CASH,),
comment='request selection or setting'),
diff --git a/src/parser-string.hh b/src/parser-string.hh
index 746051e4..45472fb2 100644
--- a/src/parser-string.hh
+++ b/src/parser-string.hh
@@ -35,16 +35,7 @@ typedef struct vte_seq_string_t {
#define VTE_SEQ_STRING_DEFAULT_CAPACITY (1 << 7) /* must be power of two */
-#ifdef WITH_SIXEL
-/* This needs to be somewhat large for the time being; it accommodates inline
- * graphics formats (e.g. sixels), and there is no provision for parsing
- * those incrementally yet. 8M characters is typically enough for an
- * RLE-incompressible 256-color 1920x1080 image. Since VTE seqs store 32 bits
- * per character, this corresponds to a 32MiB buffer. */
-#define VTE_SEQ_STRING_MAX_CAPACITY (1 << 23)
-#else
#define VTE_SEQ_STRING_MAX_CAPACITY (1 << 12)
-#endif
/*
* vte_seq_string_init:
diff --git a/src/ring.cc b/src/ring.cc
index ae86d23e..be639f90 100644
--- a/src/ring.cc
+++ b/src/ring.cc
@@ -1677,15 +1677,21 @@ Ring::write_contents(GOutputStream* stream,
* Append an image to the internal image list.
*/
void
-Ring::append_image (cairo_surface_t *surface, gint pixelwidth, gint pixelheight, glong left, glong top,
glong cell_width, glong cell_height)
+Ring::append_image(vte::cairo::Surface surface,
+ int pixelwidth,
+ int pixelheight,
+ long left,
+ long top,
+ long cell_width,
+ long cell_height) /* throws */
{
Image *image;
- image = new (std::nothrow) Image (vte::cairo::Surface(surface),
- m_next_image_priority++,
+ image = new (std::nothrow) Image(std::move(surface),
+ m_next_image_priority++,
pixelwidth, pixelheight,
- left, top,
- cell_width, cell_height);
+ left, top,
+ cell_width, cell_height);
if (!image)
return;
diff --git a/src/ring.hh b/src/ring.hh
index 85fd7236..d84f75eb 100644
--- a/src/ring.hh
+++ b/src/ring.hh
@@ -29,6 +29,7 @@
#include "vtestream.h"
#ifdef WITH_SIXEL
+#include "cairo-glue.hh"
#include "image.hh"
#include <map>
#endif
@@ -104,10 +105,10 @@ public:
GError** error);
#ifdef WITH_SIXEL
- void append_image (cairo_surface_t *surface,
+ void append_image (vte::cairo::Surface surface,
gint pixelwidth, gint pixelheight,
glong left, glong top,
- glong cell_width, glong cell_height);
+ glong cell_width, glong cell_height) /* throws */;
std::map<gint, vte::image::Image *> *m_image_by_top_map;
std::map<int, vte::image::Image *> *m_image_priority_map;
#endif
diff --git a/src/vte.cc b/src/vte.cc
index adc28c98..496324bf 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -2020,13 +2020,13 @@ Terminal::set_encoding(char const* charset,
*/
if (to_utf8) {
- if (primary_data_syntax() == DataSyntax::eECMA48_UTF8)
+ if (primary_data_syntax() == DataSyntax::ECMA48_UTF8)
return true;
m_converter.reset();
- m_primary_data_syntax = DataSyntax::eECMA48_UTF8;
+ m_primary_data_syntax = DataSyntax::ECMA48_UTF8;
} else {
- if (primary_data_syntax() == DataSyntax::eECMA48_PCTERM &&
+ if (primary_data_syntax() == DataSyntax::ECMA48_PCTERM &&
m_converter->charset() == charset)
return true;
@@ -2036,7 +2036,7 @@ Terminal::set_encoding(char const* charset,
return false;
m_converter = std::move(converter);
- m_primary_data_syntax = DataSyntax::eECMA48_PCTERM;
+ m_primary_data_syntax = DataSyntax::ECMA48_PCTERM;
} catch (...) {
return vte::glib::set_error_from_exception(error);
@@ -2055,7 +2055,7 @@ Terminal::set_encoding(char const* charset,
reset_decoder();
if (pty())
- pty()->set_utf8(primary_data_syntax() == DataSyntax::eECMA48_UTF8);
+ pty()->set_utf8(primary_data_syntax() == DataSyntax::ECMA48_UTF8);
_vte_debug_print(VTE_DEBUG_IO,
"Set terminal encoding to `%s'.\n",
@@ -3018,6 +3018,56 @@ not_inserted:
m_line_wrapped = line_wrapped;
}
+#ifdef WITH_SIXEL
+
+void
+Terminal::insert_image(vte::cairo::Surface image_surface) /* throws */
+{
+ if (!image_surface)
+ return;
+
+ auto const image_width_px = cairo_image_surface_get_width(image_surface.get());
+ auto const image_height_px = cairo_image_surface_get_height(image_surface.get());
+
+ /* Convert to device-compatible surface for m_widget */
+ auto device_surface =
vte::cairo::Surface{gdk_window_create_similar_surface(gtk_widget_get_window(widget()->gtk()),
+
CAIRO_CONTENT_COLOR_ALPHA,
+ image_width_px,
+ image_height_px)};
+
+ auto cr = cairo_create(device_surface.get());
+ cairo_set_source_surface(cr, image_surface.get(), 0, 0);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+
+ /* Reduce memory fragmentation by dropping the ref as soon as we no longer need it */
+ image_surface.reset();
+
+ /* FIXMEchpe: should insert a 'missing image' pattern instead! */
+ if (cairo_surface_status(device_surface.get()) != CAIRO_STATUS_SUCCESS)
+ return;
+
+ /* Calculate geometry */
+
+ auto const left = m_screen->cursor.col;
+ auto const top = m_screen->cursor.row;
+ auto const width = (image_width_px + m_cell_width - 1) / m_cell_width;
+ auto const height = (image_height_px + m_cell_height - 1) / m_cell_height;
+
+ m_screen->row_data->append_image(std::move(device_surface),
+ image_width_px,
+ image_height_px,
+ left,
+ top,
+ m_cell_width,
+ m_cell_height);
+
+ /* Erase characters under the image */
+ erase_image_rect(height, width);
+}
+
+#endif /* WITH_SIXEL */
+
guint8
Terminal::get_bidi_flags() const noexcept
{
@@ -3479,16 +3529,22 @@ Terminal::process_incoming()
}
switch (current_data_syntax()) {
- case DataSyntax::eECMA48_UTF8:
+ case DataSyntax::ECMA48_UTF8:
process_incoming_utf8(context, *chunk);
break;
#ifdef WITH_ICU
- case DataSyntax::eECMA48_PCTERM:
+ case DataSyntax::ECMA48_PCTERM:
process_incoming_pcterm(context, *chunk);
break;
#endif
+#ifdef WITH_SIXEL
+ case DataSyntax::DECSIXEL:
+ process_incoming_decsixel(context, *chunk);
+ break;
+#endif
+
default:
g_assert_not_reached();
break;
@@ -3692,7 +3748,7 @@ Terminal::process_incoming_utf8(ProcessingContext& context,
}
}
- switched_data_syntax:
+switched_data_syntax:
// Update start for data consumed
chunk.set_begin_reading(ip);
@@ -3831,6 +3887,45 @@ Terminal::process_incoming_pcterm(ProcessingContext& context,
#endif /* WITH_ICU */
+#ifdef WITH_SIXEL
+
+void
+Terminal::process_incoming_decsixel(ProcessingContext& context,
+ vte::base::Chunk& chunk)
+{
+ auto const [status, ip] = m_sixel_context->parse(chunk.begin_reading(),
+ chunk.end_reading(),
+ chunk.eos());
+
+ // Update start for data consumed
+ chunk.set_begin_reading(ip);
+
+ switch (status) {
+ case vte::sixel::Parser::ParseStatus::CONTINUE:
+ break;
+
+ case vte::sixel::Parser::ParseStatus::COMPLETE:
+ /* Like the main parser, the sequence only takes effect
+ * if introducer and terminator match (both C0 or both C1).
+ */
+ if (!m_sixel_context->is_matching_controls())
+ break;
+
+ try {
+ insert_image(m_sixel_context->image_cairo());
+ } catch (...) {
+ }
+
+ [[fallthrough]];
+ case vte::sixel::Parser::ParseStatus::ABORT:
+ m_sixel_context->reset();
+ pop_data_syntax();
+ break;
+ }
+}
+
+#endif /* WITH_SIXEL */
+
bool
Terminal::pty_io_read(int const fd,
GIOCondition const condition)
@@ -4096,17 +4191,19 @@ Terminal::send_child(std::string_view const& data)
/* Note that for backward compatibility, we need to emit the
* ::commit signal even if there is no PTY. See issue vte#222.
+ *
+ * We use the primary data syntax to decide on the format.
*/
switch (primary_data_syntax()) {
- case DataSyntax::eECMA48_UTF8:
+ case DataSyntax::ECMA48_UTF8:
emit_commit(data);
if (pty())
_vte_byte_array_append(m_outgoing, data.data(), data.size());
break;
#ifdef WITH_ICU
- case DataSyntax::eECMA48_PCTERM: {
+ case DataSyntax::ECMA48_PCTERM: {
auto converted = m_converter->convert(data);
emit_commit(converted);
@@ -7665,6 +7762,12 @@ Terminal::Terminal(vte::platform::Widget* w,
for (i = 0; i < VTE_PALETTE_SIZE; i++)
m_palette[i].sources[VTE_COLOR_SOURCE_ESCAPE].is_set = FALSE;
+ /* Dispatch unripe DCS (for now, just DECSIXEL) sequences,
+ * so we can switch data syntax and parse the contents with
+ * the SIXEL subparser.
+ */
+ m_parser.set_dispatch_unripe(true);
+
/* Set up I/O encodings. */
m_outgoing = _vte_byte_array_new();
@@ -7684,11 +7787,6 @@ Terminal::Terminal(vte::platform::Widget* w,
save_cursor(&m_normal_screen);
save_cursor(&m_alternate_screen);
-#ifdef WITH_SIXEL
- /* Initialize SIXEL color register */
- sixel_parser_set_default_color(&m_sixel_state);
-#endif
-
/* Matching data. */
m_match_span.clear(); // FIXMEchpe unnecessary
match_hilite_clear(); // FIXMEchpe unnecessary
@@ -9786,12 +9884,12 @@ void
Terminal::reset_decoder()
{
switch (primary_data_syntax()) {
- case DataSyntax::eECMA48_UTF8:
+ case DataSyntax::ECMA48_UTF8:
m_utf8_decoder.reset();
break;
#ifdef WITH_ICU
- case DataSyntax::eECMA48_PCTERM:
+ case DataSyntax::ECMA48_PCTERM:
m_converter->decoder().reset();
break;
#endif
@@ -9808,6 +9906,12 @@ Terminal::reset_data_syntax()
return;
switch (current_data_syntax()) {
+#ifdef WITH_SIXEL
+ case DataSyntax::DECSIXEL:
+ m_sixel_context->reset();
+ break;
+#endif
+
default:
break;
}
@@ -9920,8 +10024,8 @@ Terminal::reset(bool clear_tabstops,
m_modifiers = 0;
#ifdef WITH_SIXEL
- /* Reset SIXEL color register */
- sixel_parser_set_default_color(&m_sixel_state);
+ if (m_sixel_context)
+ m_sixel_context->reset_colors();
#endif
/* Reset the saved cursor. */
@@ -9934,6 +10038,11 @@ Terminal::reset(bool clear_tabstops,
/* Reset XTerm window controls */
m_xterm_wm_iconified = false;
+
+ /* When not using private colour registers, we should
+ * clear (assign to black) all SIXEL colour registers.
+ * (DEC PPLV2 § 5.8)
+ */
}
void
@@ -9979,7 +10088,7 @@ Terminal::set_pty(vte::base::Pty *new_pty)
set_size(m_column_count, m_row_count);
- if (!pty()->set_utf8(primary_data_syntax() == DataSyntax::eECMA48_UTF8)) {
+ if (!pty()->set_utf8(primary_data_syntax() == DataSyntax::ECMA48_UTF8)) {
// nothing we can do here
}
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 307c1f9e..1e01305a 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -49,7 +49,6 @@
#include "vtepcre2.h"
#include "vteregexinternal.hh"
-#include "sixelparser.hh"
#include "chunk.hh"
#include "pty.hh"
@@ -66,6 +65,10 @@
#include "icu-converter.hh"
#endif
+#ifdef WITH_SIXEL
+#include "sixel-context.hh"
+#endif
+
enum {
VTE_BIDI_FLAG_IMPLICIT = 1 << 0,
VTE_BIDI_FLAG_RTL = 1 << 1,
@@ -383,15 +386,18 @@ public:
enum class DataSyntax {
/* The primary data syntax is always one of the following: */
- eECMA48_UTF8,
+ ECMA48_UTF8,
#ifdef WITH_ICU
- eECMA48_PCTERM,
+ ECMA48_PCTERM,
#endif
/* ECMA48_ECMA35, not supported */
+
+ /* The following can never be primary data syntax: */
+ DECSIXEL,
};
- DataSyntax m_primary_data_syntax{DataSyntax::eECMA48_UTF8};
- DataSyntax m_current_data_syntax{DataSyntax::eECMA48_UTF8};
+ DataSyntax m_primary_data_syntax{DataSyntax::ECMA48_UTF8};
+ DataSyntax m_current_data_syntax{DataSyntax::ECMA48_UTF8};
auto primary_data_syntax() const noexcept { return m_primary_data_syntax; }
auto current_data_syntax() const noexcept { return m_current_data_syntax; }
@@ -438,14 +444,18 @@ public:
char const* encoding() const noexcept
{
switch (primary_data_syntax()) {
- case DataSyntax::eECMA48_UTF8: return "UTF-8";
+ case DataSyntax::ECMA48_UTF8: return "UTF-8";
#ifdef WITH_ICU
- case DataSyntax::eECMA48_PCTERM: return m_converter->charset().c_str();
+ case DataSyntax::ECMA48_PCTERM: return m_converter->charset().c_str();
#endif
default: g_assert_not_reached(); return nullptr;
}
}
+#ifdef WITH_SIXEL
+ std::unique_ptr<vte::sixel::Context> m_sixel_context{};
+#endif
+
/* Screen data. We support the normal screen, and an alternate
* screen, which seems to be a DEC-specific feature. */
VteScreen m_normal_screen;
@@ -561,7 +571,6 @@ public:
/* Inline images */
bool m_sixel_enabled{VTE_SIXEL_ENABLED_DEFAULT};
bool m_images_enabled{VTE_SIXEL_ENABLED_DEFAULT};
- sixel_state_t m_sixel_state;
bool set_sixel_enabled(bool enabled) noexcept
{
@@ -843,6 +852,10 @@ public:
bool insert,
bool invalidate_now);
+ #ifdef WITH_SIXEL
+ void insert_image(vte::cairo::Surface image_surface) /* throws */;
+ #endif
+
void invalidate_row(vte::grid::row_t row);
void invalidate_rows(vte::grid::row_t row_start,
vte::grid::row_t row_end /* inclusive */);
@@ -868,6 +881,10 @@ public:
void process_incoming_pcterm(ProcessingContext& context,
vte::base::Chunk& chunk);
#endif
+ #ifdef WITH_SIXEL
+ void process_incoming_decsixel(ProcessingContext& context,
+ vte::base::Chunk& chunk);
+ #endif
bool process(bool emit_adj_changed);
inline bool is_processing() const { return m_active_terminals_link != nullptr; }
void start_processing();
@@ -1390,6 +1407,8 @@ public:
inline void move_cursor_down(vte::grid::row_t rows);
inline void erase_characters(long count,
bool use_basic = false);
+ void erase_image_rect(vte::grid::row_t rows,
+ vte::grid::column_t columns);
inline void insert_blank_character();
template<unsigned int redbits, unsigned int greenbits, unsigned int bluebits>
diff --git a/src/vteseq.cc b/src/vteseq.cc
index 4f2d6b83..6f174502 100644
--- a/src/vteseq.cc
+++ b/src/vteseq.cc
@@ -984,6 +984,33 @@ Terminal::erase_characters(long count,
m_text_deleted_flag = TRUE;
}
+void
+Terminal::erase_image_rect(vte::grid::row_t rows,
+ vte::grid::column_t columns)
+{
+ auto const top = m_screen->cursor.row;
+
+ /* FIXMEchpe: simplify! */
+ for (auto i = 0; i < rows; ++i) {
+ auto const row = top + i;
+
+ erase_characters(columns, true);
+
+ if (row > m_screen->insert_delta - 1 &&
+ row < m_screen->insert_delta + m_row_count)
+ set_hard_wrapped(row);
+
+ if (i == rows - 1) {
+ if (m_modes_private.MINTTY_SIXEL_SCROLL_CURSOR_RIGHT())
+ move_cursor_forward(columns);
+ else
+ cursor_down(true);
+ } else {
+ cursor_down(true);
+ }
+ }
+}
+
/* Insert a blank character. */
void
Terminal::insert_blank_character()
@@ -2379,7 +2406,7 @@ Terminal::DA1(vte::parser::Sequence const& seq)
reply(seq, VTE_REPLY_DECDA1R, {65, 1,
#ifdef WITH_SIXEL
- 4,
+ m_sixel_enabled ? 4 : -2 /* skip */,
#endif
9});
}
@@ -4059,6 +4086,10 @@ Terminal::DECSCL(vte::parser::Sequence const& seq)
* args[0]: 64
* args[1]: 0
*
+ * When not using private colour registers, this
+ * must also clear (assign to black) all SIXEL
+ * colour registers. (DEC PPLV2 § 5.8)
+ *
* References: VT525
*/
#if 0
@@ -4330,113 +4361,107 @@ Terminal::DECSGR(vte::parser::Sequence const& seq)
/* TODO: consider implementing sub/superscript? */
}
-void
+bool
Terminal::DECSIXEL(vte::parser::Sequence const& seq)
{
/*
* DECSIXEL - SIXEL graphics
+ * Image data in DECSIXEL format.
+ *
+ * Arguments:
+ * args[0]: macro parameter (should always use 0 and use DECGRA instead)
+ * See DEC PPLV Table 5–2 in § 5.4.1.1 for more information.
+ * args[1]: background
+ * 0: device default (same as 2)
+ * 1: pixels with colour 0 retain the colour
+ * 2: pixels with colour 0 are set to the current background
+ * args[2]: horizontal grid size in the unit set by SSU
+ *
+ * Defaults:
+ * args[0]: 0
+ * args[0]: 2 (1 for printers)
+ * args[0]: no default
*
* References: VT330
+ * DEC PPLV2 § 5.4
*/
#ifdef WITH_SIXEL
- if (!m_sixel_enabled)
- return;
-
- unsigned char *pixels = NULL;
- auto fg = get_color(VTE_DEFAULT_FG);
- auto bg = get_color(VTE_DEFAULT_BG);
- int nfg = (fg->red >> 8) | ((fg->green >> 8) << 8) | ((fg->blue >> 8) << 16);
- int nbg = (bg->red >> 8) | ((bg->green >> 8) << 8) | ((bg->blue >> 8) << 16);
- glong left, top, width, height;
- glong pixelwidth, pixelheight;
- glong i;
- cairo_surface_t *image_surface, *surface;
- cairo_t *cr;
-
- /* This is unfortunate, but it avoids copying or measuring potentially
- * megabytes of data */
- const vte_seq_string_t *arg_str = &((*((vte::parser::Sequence &) seq).seq_ptr())->arg_str);
-
- /* Parse image */
-
- if (sixel_parser_init(&m_sixel_state, nfg, nbg,
- m_modes_private.XTERM_SIXEL_PRIVATE_COLOR_REGISTERS()) < 0) {
- sixel_parser_deinit(&m_sixel_state);
- return;
- }
- if (sixel_parser_feed(&m_sixel_state, arg_str->buf, arg_str->len) < 0) {
- sixel_parser_deinit(&m_sixel_state);
- return;
- }
- pixels = (unsigned char *)g_try_malloc(m_sixel_state.image.width * m_sixel_state.image.height * 4);
- if (!pixels) {
- sixel_parser_deinit(&m_sixel_state);
- return;
- }
- if (sixel_parser_finalize(&m_sixel_state, pixels) < 0) {
- g_free(pixels);
- sixel_parser_deinit(&m_sixel_state);
- return;
- }
- sixel_parser_deinit(&m_sixel_state);
-
- /* Calculate geometry */
-
- left = m_screen->cursor.col;
- top = m_screen->cursor.row;
- width = (m_sixel_state.image.width + m_cell_width - 1) / m_cell_width;
- height = (m_sixel_state.image.height + m_cell_height - 1) / m_cell_height;
- pixelwidth = m_sixel_state.image.width;
- pixelheight = m_sixel_state.image.height;
+ auto process_sixel = false;
+ auto mode = vte::sixel::Parser::Mode{};
+ if (m_sixel_enabled) {
+ switch (primary_data_syntax()) {
+ case DataSyntax::ECMA48_UTF8:
+ process_sixel = true;
+ mode = vte::sixel::Parser::Mode::UTF8;
+ break;
- /* Convert to device-compatible surface for m_widget */
+#ifdef WITH_ICU
+ case DataSyntax::ECMA48_PCTERM:
+ /* It's not really clear how DECSIXEL should be processed in PCTERM mode.
+ * The DEC documentation available isn't very detailed on PCTERM mode,
+ * and doesn't appear to mention its interaction with DECSIXEL at all.
+ *
+ * Since (afaik) a "real" DEC PCTERM mode only (?) translates the graphic
+ * characters, not the whole data stream, as we do, let's assume that
+ * DECSIXEL content should be processed as raw bytes, i.e. without any
+ * translation.
+ * Also, since C1 controls don't exist in PCTERM mode, let's process
+ * DECSIXEL in 7-bit mode.
+ *
+ * As an added complication, we can only switch data syntaxes if
+ * the data stream is exact, that is the charset converted has
+ * not consumed more data than we have currently read output bytes
+ * from it. So we need to check that the converter has no pending
+ * characters.
+ *
+ * Alternatively, we could just refuse to process DECSIXEL in
+ * PCTERM mode.
+ */
+ process_sixel = !m_converter->decoder().pending();
+ mode = vte::sixel::Parser::Mode::SEVENBIT;
+ break;
+#endif /* WITH_ICU */
- image_surface = cairo_image_surface_create_for_data(pixels, CAIRO_FORMAT_ARGB32, pixelwidth,
pixelheight, pixelwidth * 4);
- if (!image_surface) {
- g_free(pixels);
- return;
+ default:
+ __builtin_unreachable();
+ process_sixel = false;
+ }
}
- surface = gdk_window_create_similar_surface(gtk_widget_get_window (m_widget),
CAIRO_CONTENT_COLOR_ALPHA, pixelwidth, pixelheight);
- if (!surface) {
- cairo_surface_destroy(image_surface);
- g_free(pixels);
- return;
+ if (!process_sixel || seq.is_ripe() /* that shouldn't happen */) {
+ m_parser.ignore_until_st();
+ return false;
}
- cr = cairo_create(surface);
- cairo_set_source_surface(cr, image_surface, 0, 0);
- cairo_paint(cr);
- cairo_destroy(cr);
- cairo_surface_destroy(image_surface);
- g_free(pixels);
+ try {
+ if (!m_sixel_context)
+ m_sixel_context = std::make_unique<vte::sixel::Context>();
- /* Append image to Ring */
+ auto const fg = get_color(VTE_DEFAULT_FG);
+ auto const bg = get_color(VTE_DEFAULT_BG);
+ auto nfg = uint32_t(fg->red >> 8) | ((fg->green >> 8) << 8) | ((fg->blue >> 8) << 16) | 0xff
<< 24;
+ auto nbg = uint32_t(bg->red >> 8) | ((bg->green >> 8) << 8) | ((bg->blue >> 8) << 16) | 0xff
<< 24;
- m_screen->row_data->append_image(surface, pixelwidth, pixelheight, left, top, m_cell_width,
m_cell_height);
+ m_sixel_context->prepare(seq.st(),
+ nfg, nbg,
+ m_modes_private.XTERM_SIXEL_PRIVATE_COLOR_REGISTERS());
- /* Erase characters under the image */
+ m_sixel_context->set_mode(mode);
- for (i = 0; i < height; ++i) {
- vte::grid::row_t row = top + i;
-
- erase_characters(width, true);
-
- if (row > m_screen->insert_delta - 1
- && row < m_screen->insert_delta + m_row_count)
- set_hard_wrapped(row);
+ /* We need to reset the main parser, so that when it is in the ground state
+ * when processing returns to the primary data syntax from DECSIXEL
+ */
+ m_parser.reset();
+ push_data_syntax(DataSyntax::DECSIXEL);
- if (i == height - 1) {
- if (m_modes_private.MINTTY_SIXEL_SCROLL_CURSOR_RIGHT())
- move_cursor_forward(width);
- else
- cursor_down(true);
- } else {
- cursor_down(true);
- }
- }
+ return true; /* switching data syntax */
+ } catch (...) {
+ }
#endif /* WITH_SIXEL */
+
+ m_parser.ignore_until_st();
+ return false;
}
void
@@ -4939,6 +4964,10 @@ Terminal::DECSTR(vte::parser::Sequence const& seq)
* Perform a soft reset to the default values.
* [list of default values]
*
+ * When not using private colour registers, this
+ * must also clear (assign to black) all SIXEL
+ * colour registers. (DEC PPLV2 § 5.8)
+ *
* References: VT525
*/
@@ -6918,6 +6947,10 @@ Terminal::RIS(vte::parser::Sequence const& seq)
* Reset to initial state.
* [list of things reset]
*
+ * When not using private colour registers, this
+ * must also clear (assign to black) all SIXEL
+ * colour registers. (DEC PPLV2 § 5.8)
+ *
* References: ECMA-48 § 8.3.105
*/
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]