diff --git a/putty.h b/putty.h index 2f6e40f8..67307e67 100644 --- a/putty.h +++ b/putty.h @@ -2701,6 +2701,9 @@ Socket *platform_start_subprocess(const char *cmd, Plug *plug, #define LOW_SURROGATE_END 0xdfff #endif +/* REGIONAL INDICATOR SYMBOL LETTER A-Z */ +#define IS_REGIONAL_INDICATOR_LETTER(wc) ((unsigned)(wc) - 0x1F1E6U < 26) + /* These macros exist in the Windows API, so the environment may * provide them. If not, define them in terms of the above. */ #ifndef IS_HIGH_SURROGATE diff --git a/terminal/terminal.c b/terminal/terminal.c index 13aaeabb..4c453809 100644 --- a/terminal/terminal.c +++ b/terminal/terminal.c @@ -5998,6 +5998,10 @@ static void do_paint_draw(Terminal *term, termline *ldata, int x, int y, ldata->lattr, term->basic_erase_char.truecolour); win_draw_trust_sigil(term->win, x, y); } else { + if (ccount == 2 && + IS_REGIONAL_INDICATOR_LETTER(ch[0]) && + IS_REGIONAL_INDICATOR_LETTER(ch[1])) + attr |= ATTR_WIDE | TATTR_COMBINING; win_draw_text(term->win, x, y, ch, ccount, attr, ldata->lattr, tc); if (attr & (TATTR_ACTCURS | TATTR_PASCURS)) win_draw_cursor(term->win, x, y, ch, ccount, @@ -6264,7 +6268,7 @@ static void do_paint(Terminal *term) tc = term->erase_char.truecolour; for (j = 0; j < term->cols; j++) { unsigned long tattr, tchar; - bool break_run, do_copy; + bool break_run, do_copy, next_run_dirty = false; termchar *d = lchars + j; tattr = newline[j].attr; @@ -6300,6 +6304,29 @@ static void do_paint(Terminal *term) (j > 0 && d[-1].cc_next != 0)) break_run = true; + /* + * Break on both sides of a regional indicator letter. + */ + if (IS_REGIONAL_INDICATOR_LETTER(tchar)) { + break_run = true; + if (j+1 < term->cols) { + /* Also, check if there are any changes to whether or + * not we're drawing this and the next character as a + * single flag glyph. */ + bool flag_now = IS_REGIONAL_INDICATOR_LETTER(d[1].chr); + bool flag_before = ( + IS_REGIONAL_INDICATOR_LETTER( + term->disptext[i]->chars[j].chr) && + IS_REGIONAL_INDICATOR_LETTER( + term->disptext[i]->chars[j+1].chr) && + (term->disptext[i]->chars[j].attr & DATTR_STARTRUN)); + if (flag_now != flag_before) + next_run_dirty = true; /* must redraw this flag */ + } + } else if (j>0 && IS_REGIONAL_INDICATOR_LETTER(d[-1].chr)) { + break_run = true; + } + /* * Break on both sides of a trust sigil. */ @@ -6328,7 +6355,7 @@ static void do_paint(Terminal *term) cset = CSET_OF(tchar); if (term->ucsdata->dbcs_screenfont) last_run_dirty = dirty_run; - dirty_run = dirty_line; + dirty_run = dirty_line || next_run_dirty; } do_copy = false; @@ -6407,6 +6434,44 @@ static void do_paint(Terminal *term) copy_termchar(term->disptext[i], j, d); } } + + /* If it's a regional indicator letter, and so is the next + * one, then also step to the next one, keeping the flag + * sequence together. */ + if (IS_REGIONAL_INDICATOR_LETTER(d->chr) && + (j+1 < term->cols && IS_REGIONAL_INDICATOR_LETTER(d[1].chr))) { + j++; + d++; + + /* Set ATTR_WIDE, so that the pair is displayed as one */ + attr |= ATTR_WIDE; + + /* Include the second letter in the text buffer */ + unsigned long rchar = d->chr; +#ifdef PLATFORM_IS_UTF16 + sgrowarrayn(ch, chlen, ccount, 2); + ch[ccount++] = (wchar_t)HIGH_SURROGATE_OF(rchar); + ch[ccount++] = (wchar_t)LOW_SURROGATE_OF(rchar); +#else + sgrowarrayn(ch, chlen, ccount, 1); + ch[ccount++] = (wchar_t)rchar; +#endif + + /* Display the cursor, if it's on the right half */ + if (i == our_curs_y && j == our_curs_x) { + attr |= cursor; + term->disptext[i]->chars[j-1].attr |= cursor; + } + + if (!termchars_equal_override( + &term->disptext[i]->chars[j], + d, rchar, term->disptext[i]->chars[j-1].attr)) + dirty_run = true; + + copy_termchar(term->disptext[i], j, d); + term->disptext[i]->chars[j].attr = + term->disptext[i]->chars[j-1].attr & ~DATTR_STARTRUN; + } } if (dirty_run && ccount > 0) do_paint_draw(term, ldata, start, i, ch, ccount, attr, tc); diff --git a/test/utf8.txt b/test/utf8.txt index 76174d22..3b45f9eb 100644 --- a/test/utf8.txt +++ b/test/utf8.txt @@ -26,5 +26,5 @@ Emoji via U+FE0F: ❤️ ☺️ ☹️ (narrow, because wcwidth mishandl Dedicated emoji: 💜 🙂 🙁 (wide and should look correct) Combined via ZWJ: 👩‍💻 (PuTTY doesn't understand ZWJ) Skin tone mod: 👩🏻 👩🏿 (wcwidth doesn't know those are modifiers) -Flags: 🇬🇧 🇺🇦 🇪🇺 (also too complicated) +Flags: 🇬🇧 🇺🇦 🇪🇺 (should work in GTK 2 or better)