[vte/wip/egmont/bidi: 5/8] widget, bidi: Display according to the BiDi mapping



commit 71b72a4cc4c9a2bb73bd70c867cd50073a05ed9d
Author: Egmont Koblinger <egmont gmail com>
Date:   Sat Jun 1 15:47:16 2019 +0200

    widget,bidi: Display according to the BiDi mapping
    
    Display each row according to the BiDi treatment (shuffling, basic Arabic
    shaping) it received.

 src/vte.cc         | 247 ++++++++++++++++++++++++++++++++++++++++++-----------
 src/vtedraw.cc     |   6 ++
 src/vtedraw.hh     |   2 +
 src/vteinternal.hh |   8 +-
 4 files changed, 208 insertions(+), 55 deletions(-)
---
diff --git a/src/vte.cc b/src/vte.cc
index 2f74b869..5b61354b 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -36,6 +36,7 @@
 
 #include <vte/vte.h>
 #include "vteinternal.hh"
+#include "bidi.hh"
 #include "buffer.h"
 #include "debug.h"
 #include "vtedraw.hh"
@@ -1623,9 +1624,22 @@ Terminal::grid_coords_from_view_coords(vte::view::coords const& pos) const
 
         vte::grid::row_t row = pixel_to_row(pos.y);
 
+        /* BiDi: convert to logical column. */
+        vte::base::BidiRow const* bidirow = m_ringview.get_bidirow(confine_grid_row(row));
+        col = bidirow->vis2log(col);
+
         return vte::grid::coords(row, col);
 }
 
+vte::grid::row_t
+Terminal::confine_grid_row(vte::grid::row_t const& row) const
+{
+        auto first_row = first_displayed_row();
+        auto last_row = last_displayed_row();
+
+        return CLAMP(row, first_row, last_row);
+}
+
 /*
  * Terminal::confined_grid_coords_from_view_coords:
  * @pos: the view coordinates
@@ -1687,34 +1701,47 @@ Terminal::confine_grid_coords(vte::grid::coords const& rowcol) const
 
 /*
  * Track mouse click and drag positions (the "origin" and "last" coordinates) with half cell accuracy,
- * that is, know whether the event occurred over the left or right half of the cell.
+ * that is, know whether the event occurred over the left/start or right/end half of the cell.
  * This is required because some selection modes care about the cell over which the event occurred,
  * while some care about the closest boundary between cells.
  *
  * Storing the actual view coordinates would become problematic when the font size changes (bug 756058),
  * and would cause too much work when the mouse moves within the half cell.
  *
- * Left margin or anything further to the left is denoted by column -1's right half,
- * right margin or anything further to the right is denoted by column m_column_count's left half.
+ * Left/start margin or anything further to the left/start is denoted by column -1's right half,
+ * right/end margin or anything further to the right/end is denoted by column m_column_count's left half.
+ *
+ * BiDi: returns logical position (start or end) for normal selection modes, visual position (left or
+ * right) for block mode.
  */
 vte::grid::halfcoords
 Terminal::selection_grid_halfcoords_from_view_coords(vte::view::coords const& pos) const
 {
         vte::grid::row_t row = pixel_to_row(pos.y);
-        vte::grid::halfcolumn_t halfcolumn;
+        vte::grid::column_t col;
+        vte::grid::half_t half;
 
         if (pos.x < 0) {
-                halfcolumn.set_column(-1);
-                halfcolumn.set_half(1);
+                col = -1;
+                half = 1;
         } else if (pos.x >= m_column_count * m_cell_width) {
-                halfcolumn.set_column(m_column_count);
-                halfcolumn.set_half(0);
+                col = m_column_count;
+                half = 0;
         } else {
-                halfcolumn.set_column(pos.x / m_cell_width);
-                halfcolumn.set_half((pos.x * 2 / m_cell_width) % 2);
+                col = pos.x / m_cell_width;
+                half = (pos.x * 2 / m_cell_width) % 2;
+        }
+
+        if (!m_selection_block_mode) {
+                /* BiDi: convert from visual to logical half column. */
+                vte::base::BidiRow const* bidirow = m_ringview.get_bidirow(confine_grid_row(row));
+
+                if (bidirow->vis_is_rtl(col))
+                        half = 1 - half;
+                col = bidirow->vis2log(col);
         }
 
-        return { row, halfcolumn };
+        return { row, vte::grid::halfcolumn_t(col, half) };
 }
 
 /*
@@ -5305,7 +5332,7 @@ Terminal::line_is_wrappable(vte::grid::row_t row) const
  * In block mode, similarly to char mode, we care about vertical character boundary. (This is somewhat
  * debatable, as results in asymmetrical behavior along the two axes: a rectangle can disappear by
  * becoming zero wide, but not zero high.) We cannot take care of CJKs at the endpoints now because CJKs
- * can cross the boundary in any included row. Taking care of them needs to go to cell_is_selected().
+ * can cross the boundary in any included row. Taking care of them needs to go to cell_is_selected_vis().
  * We don't care about used vs. unused cells either. The event coordinate is simply rounded to the
  * nearest vertical cell boundary.
  */
@@ -5604,21 +5631,29 @@ Terminal::modify_selection (vte::view::coords const& pos)
         resolve_selection();
 }
 
-/* Check if a cell is selected or not. */
+/* Check if a cell is selected or not. BiDi: the coordinate is visual. */
 bool
-Terminal::cell_is_selected(vte::grid::column_t col,
-                                     vte::grid::row_t row) const
+Terminal::cell_is_selected_vis(vte::grid::column_t col,
+                               vte::grid::row_t row) const
 {
         if (m_selection_block_mode) {
                 /* In block mode, make sure CJKs and TABs aren't cut in half. */
+                /* BiDi: convert to logical column... */
+                vte::base::BidiRow const* bidirow = m_ringview.get_bidirow(row);
+                col = bidirow->vis2log(col);
                 while (col > 0) {
                         VteCell const* cell = find_charcell(col, row);
                         if (!cell || !cell->attr.fragment())
                                 break;
                         col--;
                 }
+                /* ... and back to visual. */
+                col = bidirow->log2vis(col);
                 return m_selection_resolved.box_contains ({ row, col });
         } else {
+                /* BiDi: convert to logical column. */
+                vte::base::BidiRow const* bidirow = m_ringview.get_bidirow(row);
+                col = bidirow->vis2log(col);
                 /* In normal modes, resolve_selection() made sure to generate such boundaries for 
m_selection_resolved. */
                 return m_selection_resolved.contains ({ row, col });
         }
@@ -6080,6 +6115,10 @@ Terminal::match_hilite_update()
         glong col = pos.x / m_cell_width;
         glong row = pixel_to_row(pos.y);
 
+        /* BiDi: convert to logical column. */
+        vte::base::BidiRow const* bidirow = m_ringview.get_bidirow(confine_grid_row(row));
+        col = bidirow->vis2log(col);
+
        _vte_debug_print(VTE_DEBUG_EVENTS,
                          "Match hilite update (%ld, %ld) -> %ld, %ld\n",
                          pos.x, pos.y, col, row);
@@ -6284,6 +6323,9 @@ Terminal::get_text(vte::grid::row_t start_row,
        GString *string;
        struct _VteCharAttributes attr;
        vte::color::rgb fore, back;
+        vte::base::RingView *ringview = nullptr;
+        vte::base::BidiRow const *bidirow = nullptr;
+        vte::grid::column_t col_vis;
 
        if (attributes)
                g_array_set_size (attributes, 0);
@@ -6294,26 +6336,50 @@ Terminal::get_text(vte::grid::row_t start_row,
         if (start_col < 0)
                 start_col = 0;
 
-        vte::grid::column_t next_first_column = block ? start_col : 0;
-        vte::grid::column_t col = start_col;
+        if (block) {
+                /* Rectangular selection operates on the visual contents, not the logical.
+                 * m_ringview corresponds to the currently onscreen bits, therefore does not
+                 * necessarily include the entire selection. Also we want m_ringview's size
+                 * to be limited, even if the user selects a giant rectangle.
+                 * So use a new ringview for the selection. */
+                ringview = new vte::base::RingView();
+                ringview->set_ring(m_screen->row_data);
+                ringview->set_rows(start_row, end_row - start_row + 1);
+                ringview->set_width(m_column_count);
+                ringview->update();
+        }
+
+        vte::grid::column_t col = block ? 0 : start_col;
         vte::grid::row_t row;
-       for (row = start_row; row < end_row + 1; row++, col = next_first_column) {
+        for (row = start_row; row < end_row + 1; row++, col = 0) {
                VteRowData const* row_data = find_row_data(row);
                 gsize last_empty, last_nonempty;
                 vte::grid::column_t last_emptycol, last_nonemptycol;
-                vte::grid::column_t line_last_column = (block || row == end_row) ? end_col : G_MAXLONG;
+                vte::grid::column_t line_last_column = (!block && row == end_row) ? end_col : m_column_count;
 
                 last_empty = last_nonempty = string->len;
                 last_emptycol = last_nonemptycol = -1;
 
                attr.row = row;
-               attr.column = col;
+                attr.column = col;  // FIXME is attr.column supposed to contain logical or visual? What is 
it used for?
                pcell = NULL;
                if (row_data != NULL) {
+                        bidirow = ringview ? ringview->get_bidirow(row) : nullptr;
                         while (col < line_last_column &&
                                (pcell = _vte_row_data_get (row_data, col))) {
 
-                               attr.column = col;
+                                /* In block mode, we scan each row from its very beginning to its very end 
in logical order,
+                                 * and here filter out the characters that are visually outside of the 
block. */
+                                if (bidirow) {
+                                        col_vis = bidirow->log2vis(col);
+                                        // FIXME handle CJK and friends consistently with cell_is_selected().
+                                        if (col_vis < start_col || col_vis >= end_col) {
+                                                col++;
+                                                continue;
+                                        }
+                                }
+
+                                attr.column = col;  // FIXME ditto
 
                                /* If it's not part of a multi-column character,
                                 * and passes the selection criterion, add it to
@@ -6412,6 +6478,8 @@ Terminal::get_text(vte::grid::row_t start_row,
                }
        }
 
+        delete ringview;
+
        /* Sanity check. */
         if (attributes != nullptr)
                 g_assert_cmpuint(string->len, ==, attributes->len);
@@ -9034,6 +9102,10 @@ Terminal::ringview_update()
         m_ringview.update ();
 }
 
+/* XXX tmp hack */
+#define _vte_row_data_get_visual(row_data_p, bidimap, col) \
+        row_data_p == nullptr ? nullptr : _vte_row_data_get(row_data_p, bidimap->vis2log(col))
+
 /* Paint the contents of a given row at the given location.  Take advantage
  * of multiple-draw APIs by finding runs of characters with identical
  * attributes and bundling them together. */
@@ -9048,15 +9120,17 @@ Terminal::draw_rows(VteScreen *screen_,
 {
         struct _vte_draw_text_request *items;
         vte::grid::row_t row;
-        vte::grid::column_t i, j, col;
+        vte::grid::column_t i, j, lcol, vcol;
         int y;
         guint fore = VTE_DEFAULT_FG, nfore, back = VTE_DEFAULT_BG, nback, deco = VTE_DEFAULT_FG, ndeco;
         gboolean hyperlink = FALSE, nhyperlink, hilite = FALSE, nhilite;
         gboolean selected;
+        gboolean nrtl = FALSE, rtl;  /* for debugging */
         uint32_t attr = 0, nattr;
        guint item_count;
        const VteCell *cell;
        VteRowData const* row_data;
+        vte::base::BidiRow const* bidirow;
 
         auto const column_count = m_column_count;
         uint32_t const attr_mask = m_allow_bold ? ~0 : ~VTE_ATTR_BOLD_MASK;
@@ -9084,25 +9158,28 @@ Terminal::draw_rows(VteScreen *screen_,
                 if (row_data == nullptr)
                         continue; /* Skip row. FIXME: just paint this row empty? */
 
+                bidirow = m_ringview.get_bidirow(row);
                 i = j = 0;
                 /* Walk the line.
                  * Locate runs of identical bg colors within a row, and paint each run as a single 
rectangle. */
                 do {
                         /* Get the first cell's contents. */
-                        cell = row_data ? _vte_row_data_get (row_data, i) : nullptr;
+                        cell = row_data ? _vte_row_data_get_visual (row_data, bidirow, i) : nullptr;
                         /* Find the colors for this cell. */
-                        selected = cell_is_selected(i, row);
+                        selected = cell_is_selected_vis(i, row);
                         determine_colors(cell, selected, &fore, &back, &deco);
+                        rtl = bidirow->vis_is_rtl(i);
 
                         while (++j < column_count) {
                                 /* Retrieve the next cell. */
-                                cell = row_data ? _vte_row_data_get (row_data, j) : nullptr;
+                                cell = row_data ? _vte_row_data_get_visual (row_data, bidirow, j) : nullptr;
                                 /* Resolve attributes to colors where possible and
                                  * compare visual attributes to the first character
                                  * in this chunk. */
-                                selected = cell_is_selected(j, row);
+                                selected = cell_is_selected_vis(j, row);
                                 determine_colors(cell, selected, &nfore, &nback, &ndeco);
-                                if (nback != back) {
+                                nrtl = bidirow->vis_is_rtl(j);
+                                if (nback != back || (_vte_debug_on (VTE_DEBUG_BIDI) && nrtl != rtl)) {
                                         break;
                                 }
                         }
@@ -9116,6 +9193,21 @@ Terminal::draw_rows(VteScreen *screen_,
                                                           row_height,
                                                           &bg, VTE_DRAW_OPAQUE);
                         }
+                        if (G_UNLIKELY (_vte_debug_on (VTE_DEBUG_BIDI) && rtl)) {
+                                /* Debug: Highlight RTL letters with a slightly different background. */
+                                vte::color::rgb bg;
+                                rgb_from_index<8, 8, 8>(back, bg);
+                                bg.red   = 0xC000 + (bg.red   - 0xC000) / 2;
+                                bg.green = 0xC000 + (bg.green - 0xC000) / 2;
+                                bg.blue  = 0xC000 + (bg.blue  - 0xC000) / 2;
+                                _vte_draw_fill_rectangle (
+                                                m_draw,
+                                                i * column_width,
+                                                y + row_height / 8,
+                                                (j - i) * column_width,
+                                                row_height * 3 / 4,
+                                                &bg, VTE_DRAW_OPAQUE);
+                        }
                         /* We'll need to continue at the first cell which didn't
                          * match the first one in this set. */
                         i = j;
@@ -9146,16 +9238,17 @@ Terminal::draw_rows(VteScreen *screen_,
                 /* Ensure that drawing is restricted to the cell (plus the overdraw area) */
                 _vte_draw_autoclip_t clipper{m_draw, &rect};
 
-                /* Walk the line.
+                bidirow = m_ringview.get_bidirow(row);
+
+                /* Walk the line in logical order.
                  * Locate runs of identical attributes within a row, and draw each run using a single 
draw_cells() call. */
                 item_count = 0;
-                for (col = 0; col < column_count; ) {
+                for (lcol = 0; lcol < row_data->len && lcol < column_count; ) {
+                        vcol = bidirow->log2vis(lcol);
+
                         /* Get the character cell's contents. */
-                        cell = _vte_row_data_get (row_data, col);
-                        if (cell == NULL) {
-                                /* There'll be no more real cells in this row. */
-                                break;
-                        }
+                        cell = _vte_row_data_get (row_data, lcol);
+                        g_assert(cell != nullptr);
 
                         nhyperlink = (m_allow_hyperlink && cell->attr.hyperlink_idx != 0);
                         if (cell->c == 0 ||
@@ -9167,17 +9260,17 @@ Terminal::draw_rows(VteScreen *screen_,
                             cell->attr.fragment() ||
                             cell->attr.invisible()) {
                                 /* Skip empty or fragment cell. */
-                                col++;
+                                lcol++;
                                 continue;
                         }
 
                         /* Find the colors for this cell. */
                         nattr = cell->attr.attr;
-                        selected = cell_is_selected(col, row);
+                        selected = cell_is_selected_vis(vcol, row);
                         determine_colors(cell, selected, &nfore, &nback, &ndeco);
 
                         nhilite = (nhyperlink && cell->attr.hyperlink_idx == m_hyperlink_hover_idx) ||
-                                  (!nhyperlink && m_match != nullptr && m_match_span.contains(row, col));
+                                  (!nhyperlink && m_match != nullptr && m_match_span.contains(row, lcol));
 
                         /* See if it no longer fits the run. */
                         if (item_count > 0 &&
@@ -9204,16 +9297,16 @@ Terminal::draw_rows(VteScreen *screen_,
 
                         /* Combine with subsequent spacing marks. */
                         vteunistr c = cell->c;
-                        j = col + cell->attr.columns();
-                        if (G_UNLIKELY (col == 0 && g_unichar_ismark (_vte_unistr_get_base (cell->c)))) {
+                        j = lcol + cell->attr.columns();
+                        if (G_UNLIKELY (lcol == 0 && g_unichar_ismark (_vte_unistr_get_base (cell->c)))) {
                                 /* A rare special case: the first cell contains a spacing mark.
                                  * Place on top of a NBSP, along with additional spacing marks if any,
                                  * and display beginning at offscreen column -1.
                                  * Additional spacing marks, if any, will be combined by the loop below. */
                                 c = _vte_unistr_append_unistr (0x00A0, cell->c);
-                                col = -1;
+                                lcol = -1;
                         }
-                        while (j < m_column_count) {
+                        while (j < row_data->len && j < column_count) {
                                 /* Combine with subsequent spacing marks. */
                                 cell = _vte_row_data_get (row_data, j);
                                 if (cell && !cell->attr.fragment() && g_unichar_ismark (_vte_unistr_get_base 
(cell->c))) {
@@ -9224,6 +9317,33 @@ Terminal::draw_rows(VteScreen *screen_,
                                 }
                         }
 
+                        /* There are certain mandatory ligatures in Arabic, e.g.
+                         * LAM U+0644 (on the right) + ALEF U+0627 (on the left)
+                         * needs to become a single symbol (U+FEFB or U+FEFC in the
+                         * legacy presentation form characters).
+                         * FriBidi replaces the first (right) character with ZWNBSP U+FEFF and
+                         * the second (left) character with the desired presentational form.
+                         * Detect if we're about to render this glyph, and shift by half a cell
+                         * to the right. */
+                        // FIXME Get FriBidi devs document this behavior.
+                        // FIXME Check what Arabic monospace fonts do. Is there any where these
+                        // ligated glyphs are wide, thus we now break its positioning?
+                        vteunistr c_shaped = bidirow->vis_get_shaped_char(vcol, c);
+                        int xoff = 0;
+                        if (G_UNLIKELY (c_shaped != c && lcol > 0 && vcol < column_count - 1)) {
+                                int vcol2 = vcol + 1;
+                                int lcol2 = bidirow->vis2log(vcol2);
+                                if (lcol2 == lcol - 1) {
+                                        const VteCell *cell2 = _vte_row_data_get (row_data, lcol2);
+                                        g_assert(cell2 != nullptr);
+                                        vteunistr c2 = cell2->c;
+                                        vteunistr c2_shaped = bidirow->vis_get_shaped_char(vcol2, c2);
+                                        if (c2 != 0xFEFF && c2_shaped == 0xFEFF) {
+                                                xoff = column_width / 2;
+                                        }
+                                }
+                        }
+
                         attr = nattr;
                         fore = nfore;
                         back = nback;
@@ -9232,14 +9352,16 @@ Terminal::draw_rows(VteScreen *screen_,
                         hilite = nhilite;
 
                         g_assert_cmpint (item_count, <, column_count);
-                        items[item_count].c = c;
-                        items[item_count].columns = j - col;
-                        items[item_count].x = col * column_width;
+                        items[item_count].c = bidirow->vis_get_shaped_char(vcol, c);
+                        items[item_count].columns = j - lcol;
+                        items[item_count].x = (vcol - (bidirow->vis_is_rtl(vcol) ? items[item_count].columns 
- 1 : 0)) * column_width + xoff;
                         items[item_count].y = y;
+                        items[item_count].mirror = bidirow->vis_is_rtl(vcol);
+                        items[item_count].box_mirror = !!(row_data->attr.bidi_flags & 
VTE_BIDI_FLAG_BOX_MIRROR);
                         item_count++;
 
-                        g_assert_cmpint (j, >, col);
-                        col = j;
+                        g_assert_cmpint (j, >, lcol);
+                        lcol = j;
                 }
 
                 /* Draw the last run of cells in the row. */
@@ -9258,7 +9380,7 @@ Terminal::paint_cursor()
 {
        struct _vte_draw_text_request item;
         vte::grid::row_t drow;
-        vte::grid::column_t col;
+        vte::grid::column_t col, viscol;
         int width, height, cursor_width;
         guint style = 0;
         guint fore, back, deco;
@@ -9291,6 +9413,9 @@ Terminal::paint_cursor()
 
         /* Find the first cell of the character "under" the cursor.
          * This is for CJK.  For TAB, paint the cursor where it really is. */
+        VteRowData const *row_data = find_row_data(drow);
+        vte::base::BidiRow const *bidirow = m_ringview.get_bidirow(drow);
+
        auto cell = find_charcell(col, drow);
         while (cell != NULL && cell->attr.fragment() && cell->c != '\t' && col > 0) {
                col--;
@@ -9298,15 +9423,18 @@ Terminal::paint_cursor()
        }
 
        /* Draw the cursor. */
-       item.c = (cell && cell->c) ? cell->c : ' ';
+        viscol = bidirow->log2vis(col);
+        item.c = (cell && cell->c) ? bidirow->vis_get_shaped_char(viscol, cell->c) : ' ';
        item.columns = item.c == '\t' ? 1 : cell ? cell->attr.columns() : 1;
-       item.x = col * width;
+        item.x = (viscol - ((cell && bidirow->vis_is_rtl(viscol)) ? cell->attr.columns() - 1 : 0)) * width;
        item.y = row_to_pixel(drow);
+        item.mirror = bidirow->vis_is_rtl(viscol);
+        item.box_mirror = (row_data && (row_data->attr.bidi_flags & VTE_BIDI_FLAG_BOX_MIRROR));
        if (cell && cell->c != 0) {
                style = _vte_draw_get_style(cell->attr.bold(), cell->attr.italic());
        }
 
-       selected = cell_is_selected(col, drow);
+        selected = cell_is_selected_vis(viscol, drow);
         determine_cursor_colors(cell, selected, &fore, &back, &deco);
         rgb_from_index<8, 8, 8>(back, bg);
 
@@ -9328,9 +9456,17 @@ Terminal::paint_cursor()
                         stem_width = (int) (((float) (m_char_ascent + m_char_descent)) * 
m_cursor_aspect_ratio + 0.5);
                         stem_width = CLAMP (stem_width, VTE_LINE_WIDTH, m_cell_width);
 
+                        // FIXME make an exception when the cursor is just after the last character:
+                        // show it next to the last character (wherever it appears due to BiDi) rather than
+                        // beyond the end of the string, as KDE/Qt does. Maybe not just for I-beam.
+
+                        if (row_data && bidirow->vis_is_rtl(viscol))
+                                x += item.columns * m_cell_width - stem_width;
+
                         _vte_draw_fill_rectangle(m_draw,
                                                  x, y + m_char_padding.top, stem_width, m_char_ascent + 
m_char_descent,
                                                  &bg, VTE_DRAW_OPAQUE);
+
                        break;
                 }
 
@@ -9414,12 +9550,19 @@ void
 Terminal::paint_im_preedit_string()
 {
        int col, columns;
+        long row;
        long width, height;
        int i, len;
 
        if (m_im_preedit.empty())
                return;
 
+        /* Get the row's BiDi information. */
+        row = m_screen->cursor.row;
+        if (row < first_displayed_row() || row > last_displayed_row())
+                return;
+        vte::base::BidiRow const *bidirow = m_ringview.get_bidirow(row);
+
        /* Keep local copies of rendering information. */
        width = m_cell_width;
        height = m_cell_height;
@@ -9430,7 +9573,7 @@ Terminal::paint_im_preedit_string()
 
        /* If the pre-edit string won't fit on the screen if we start
         * drawing it at the cursor's position, move it left. */
-        col = m_screen->cursor.col;
+        col = bidirow->log2vis(m_screen->cursor.col);
        if (col + columns > m_column_count) {
                col = MAX(0, m_column_count - columns);
        }
@@ -9441,7 +9584,7 @@ Terminal::paint_im_preedit_string()
                const char *preedit = m_im_preedit.c_str();
                int preedit_cursor;
 
-               items = g_new(struct _vte_draw_text_request, len);
+                items = g_new0(struct _vte_draw_text_request, len);
                for (i = columns = 0; i < len; i++) {
                        items[i].c = g_utf8_get_char(preedit);
                         items[i].columns = _vte_unichar_width(items[i].c,
diff --git a/src/vtedraw.cc b/src/vtedraw.cc
index 215e27ee..6314c956 100644
--- a/src/vtedraw.cc
+++ b/src/vtedraw.cc
@@ -26,6 +26,7 @@
 #include <glib.h>
 #include <gtk/gtk.h>
 
+#include "bidi.hh"
 #include "vtedraw.hh"
 #include "vtedefines.hh"
 #include "debug.h"
@@ -1487,6 +1488,11 @@ _vte_draw_text_internal (struct _vte_draw *draw,
 
        for (i = 0; i < n_requests; i++) {
                vteunistr c = requests[i].c;
+
+                if (G_UNLIKELY (requests[i].mirror)) {
+                        vte_bidi_get_mirror_char (c, requests[i].box_mirror, &c);
+                }
+
                struct unistr_info *uinfo = font_info_get_unistr_info (font, c);
                union unistr_font_info *ufi = &uinfo->ufi;
                 int x, y;
diff --git a/src/vtedraw.hh b/src/vtedraw.hh
index 7352d376..73dcdae1 100644
--- a/src/vtedraw.hh
+++ b/src/vtedraw.hh
@@ -44,6 +44,8 @@ struct _vte_draw;
 struct _vte_draw_text_request {
        vteunistr c;
        gshort x, y, columns;
+        guint8 mirror : 1;
+        guint8 box_mirror : 1;
 };
 
 guint _vte_draw_get_style(gboolean bold, gboolean italic);
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index a8e267e4..cc8280bb 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -24,6 +24,7 @@
 #include "vtetypes.hh"
 #include "vtedraw.hh"
 #include "reaper.hh"
+#include "bidi.hh"
 #include "ring.hh"
 #include "ringview.hh"
 #include "buffer.h"
@@ -401,7 +402,7 @@ public:
         gboolean m_selecting_had_delta;
         gboolean m_selection_block_mode;  // FIXMEegmont move it into a 4th value in vte_selection_type?
         enum vte_selection_type m_selection_type;
-        vte::grid::halfcoords m_selection_origin, m_selection_last;
+        vte::grid::halfcoords m_selection_origin, m_selection_last;  /* BiDi: logical in normal modes, 
visual in m_selection_block_mode */
         vte::grid::span m_selection_resolved;
 
        /* Clipboard data information. */
@@ -732,6 +733,7 @@ public:
 
         inline bool grid_coords_in_scrollback(vte::grid::coords const& rowcol) const { return rowcol.row() < 
m_screen->insert_delta; }
 
+        vte::grid::row_t confine_grid_row(vte::grid::row_t const& row) const;
         vte::grid::coords confine_grid_coords(vte::grid::coords const& rowcol) const;
         vte::grid::coords confined_grid_coords_from_event(GdkEvent const* event) const;
         vte::grid::coords confined_grid_coords_from_view_coords(vte::view::coords const& pos) const;
@@ -956,8 +958,8 @@ public:
         void resolve_selection();
         void selection_maybe_swap_endpoints(vte::view::coords const& pos);
         void modify_selection(vte::view::coords const& pos);
-        bool cell_is_selected(vte::grid::column_t col,
-                              vte::grid::row_t) const;
+        bool cell_is_selected_vis(vte::grid::column_t col,
+                                  vte::grid::row_t) const;
 
         void reset_default_attributes(bool reset_hyperlink);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]