Terminal core (src/core, QtCore only, headless testable): - VT100/xterm parser: CSI/OSC/DCS, SGR 256/truecolor, scroll regions, insert/delete, DEC special graphics, alternate screen - cell grid with 5000 line scrollback, CJK wide chars, combining marks - xterm 256 color palette Terminal widget (src/term): - QPainter renderer with run batching, wide char clipping, cursor blink - mouse selection (char/word/line), auto copy, middle click paste - full key encoding: arrows + DECCKM, F1-F12, modifiers, ctrl/alt - IME support via inputMethodEvent Transports (src/session): - SSH through the system OpenSSH client on a forkpty pseudo terminal, so agent, ProxyJump, known_hosts and 2FA keep working - Telnet with IAC negotiation (NAWS, ECHO, SGA) - Serial via QSerialPort UI (src/ui): - tabbed main window, session dialog with saved profiles - dark violet theme, CLI targets (user@host:port, telnet://, serial:) Tests: - tests/test_core: escape sequence and layout assertions - tests/test_widget: offscreen render checks and key encoding
126 lines
6.2 KiB
C++
126 lines
6.2 KiB
C++
#include "CharWidth.h"
|
|
|
|
namespace vx {
|
|
|
|
namespace {
|
|
|
|
struct Range {
|
|
char32_t lo;
|
|
char32_t hi;
|
|
};
|
|
|
|
constexpr Range kZeroWidth[] = {
|
|
{0x00AD, 0x00AD}, {0x0300, 0x036F}, {0x0483, 0x0489}, {0x0591, 0x05BD},
|
|
{0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C5}, {0x05C7, 0x05C7},
|
|
{0x0610, 0x061A}, {0x064B, 0x065F}, {0x0670, 0x0670}, {0x06D6, 0x06DC},
|
|
{0x06DF, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, {0x0711, 0x0711},
|
|
{0x0730, 0x074A}, {0x07A6, 0x07B0}, {0x07EB, 0x07F3}, {0x0816, 0x082D},
|
|
{0x0859, 0x085B}, {0x08D3, 0x0903}, {0x093A, 0x093C}, {0x0941, 0x0948},
|
|
{0x094D, 0x094D}, {0x0951, 0x0957}, {0x0962, 0x0963}, {0x0981, 0x0981},
|
|
{0x09BC, 0x09BC}, {0x09C1, 0x09C4}, {0x09CD, 0x09CD}, {0x0A01, 0x0A02},
|
|
{0x0A3C, 0x0A3C}, {0x0A41, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D},
|
|
{0x0A70, 0x0A71}, {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC}, {0x0AC1, 0x0AC8},
|
|
{0x0ACD, 0x0ACD}, {0x0B01, 0x0B01}, {0x0B3C, 0x0B3C}, {0x0B3F, 0x0B3F},
|
|
{0x0B41, 0x0B44}, {0x0B4D, 0x0B4D}, {0x0B82, 0x0B82}, {0x0BC0, 0x0BC0},
|
|
{0x0BCD, 0x0BCD}, {0x0C00, 0x0C00}, {0x0C3E, 0x0C40}, {0x0C46, 0x0C48},
|
|
{0x0C4A, 0x0C4D}, {0x0CBC, 0x0CBC}, {0x0CCC, 0x0CCD}, {0x0D41, 0x0D44},
|
|
{0x0D4D, 0x0D4D}, {0x0DCA, 0x0DCA}, {0x0DD2, 0x0DD4}, {0x0E31, 0x0E31},
|
|
{0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9},
|
|
{0x0EBB, 0x0EBC}, {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35},
|
|
{0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F71, 0x0F7E}, {0x0F80, 0x0F84},
|
|
{0x0F86, 0x0F87}, {0x0F8D, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6},
|
|
{0x102D, 0x1030}, {0x1032, 0x1037}, {0x1039, 0x103A}, {0x1058, 0x1059},
|
|
{0x1160, 0x11FF}, {0x135D, 0x135F}, {0x1712, 0x1714}, {0x1732, 0x1734},
|
|
{0x1752, 0x1753}, {0x1772, 0x1773}, {0x17B4, 0x17B5}, {0x17B7, 0x17BD},
|
|
{0x17C6, 0x17C6}, {0x17C9, 0x17D3}, {0x17DD, 0x17DD}, {0x180B, 0x180D},
|
|
{0x18A9, 0x18A9}, {0x1920, 0x1922}, {0x1927, 0x1928}, {0x1932, 0x1932},
|
|
{0x1939, 0x193B}, {0x1A17, 0x1A18}, {0x1A1B, 0x1A1B}, {0x1A56, 0x1A56},
|
|
{0x1A60, 0x1A60}, {0x1A62, 0x1A62}, {0x1A65, 0x1A6C}, {0x1A73, 0x1A7C},
|
|
{0x1A7F, 0x1A7F}, {0x1AB0, 0x1AFF}, {0x1B00, 0x1B03}, {0x1B34, 0x1B34},
|
|
{0x1B36, 0x1B3A}, {0x1B3C, 0x1B3C}, {0x1B42, 0x1B42}, {0x1B6B, 0x1B73},
|
|
{0x1DC0, 0x1DFF}, {0x200B, 0x200F}, {0x2028, 0x202E}, {0x2060, 0x2064},
|
|
{0x2066, 0x206F}, {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2D7F, 0x2D7F},
|
|
{0x302A, 0x302D}, {0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D},
|
|
{0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA802, 0xA802}, {0xA806, 0xA806},
|
|
{0xA80B, 0xA80B}, {0xA825, 0xA826}, {0xA8C4, 0xA8C5}, {0xA8E0, 0xA8F1},
|
|
{0xA926, 0xA92D}, {0xA947, 0xA951}, {0xA980, 0xA982}, {0xA9B3, 0xA9B3},
|
|
{0xA9B6, 0xA9B9}, {0xA9BC, 0xA9BD}, {0xAA29, 0xAA2E}, {0xAA31, 0xAA32},
|
|
{0xAA35, 0xAA36}, {0xAA43, 0xAA43}, {0xAA4C, 0xAA4C}, {0xAAB0, 0xAAB0},
|
|
{0xAAB2, 0xAAB4}, {0xAAB7, 0xAAB8}, {0xAABE, 0xAABF}, {0xAAC1, 0xAAC1},
|
|
{0xAAEC, 0xAAED}, {0xAAF6, 0xAAF6}, {0xABE5, 0xABE5}, {0xABE8, 0xABE8},
|
|
{0xABED, 0xABED}, {0xFB1E, 0xFB1E}, {0xFE00, 0xFE0F}, {0xFE20, 0xFE2F},
|
|
{0xFEFF, 0xFEFF}, {0x101FD, 0x101FD}, {0x10A01, 0x10A0F}, {0x11001, 0x11001},
|
|
{0x11038, 0x11046}, {0x1D167, 0x1D169}, {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B},
|
|
{0x1D1AA, 0x1D1AD}, {0xE0100, 0xE01EF},
|
|
};
|
|
|
|
constexpr Range kWide[] = {
|
|
{0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, {0x23E9, 0x23EC},
|
|
{0x23F0, 0x23F0}, {0x23F3, 0x23F3}, {0x25FD, 0x25FE}, {0x2614, 0x2615},
|
|
{0x2648, 0x2653}, {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1},
|
|
{0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, {0x26CE, 0x26CE},
|
|
{0x26D4, 0x26D4}, {0x26EA, 0x26EA}, {0x26F2, 0x26F3}, {0x26F5, 0x26F5},
|
|
{0x26FA, 0x26FA}, {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B},
|
|
{0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755},
|
|
{0x2757, 0x2757}, {0x2795, 0x2797}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF},
|
|
{0x2B1B, 0x2B1C}, {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2EF3},
|
|
{0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, {0x3000, 0x303E}, {0x3041, 0x3096},
|
|
{0x3099, 0x30FF}, {0x3105, 0x312D}, {0x3131, 0x318E}, {0x3190, 0x31BA},
|
|
{0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x32FE},
|
|
{0x3300, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C},
|
|
{0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52},
|
|
{0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6},
|
|
{0x16FE0, 0x16FE4}, {0x17000, 0x187F7}, {0x18800, 0x18CD5}, {0x1B000, 0x1B152},
|
|
{0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF},
|
|
{0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, {0x1F200, 0x1F320}, {0x1F32D, 0x1F335},
|
|
{0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, {0x1F3A0, 0x1F3CA}, {0x1F3CF, 0x1F3D3},
|
|
{0x1F3E0, 0x1F3F0}, {0x1F3F4, 0x1F3F4}, {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440},
|
|
{0x1F442, 0x1F4FC}, {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, {0x1F550, 0x1F567},
|
|
{0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4}, {0x1F5FB, 0x1F64F},
|
|
{0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, {0x1F6D0, 0x1F6D2}, {0x1F6D5, 0x1F6D7},
|
|
{0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6FC}, {0x1F7E0, 0x1F7EB}, {0x1F90C, 0x1F93A},
|
|
{0x1F93C, 0x1F945}, {0x1F947, 0x1F978}, {0x1F97A, 0x1F9CB}, {0x1F9CD, 0x1F9FF},
|
|
{0x1FA70, 0x1FA74}, {0x1FA78, 0x1FA7A}, {0x1FA80, 0x1FA86}, {0x1FA90, 0x1FAA8},
|
|
{0x1FAB0, 0x1FAB6}, {0x1FAC0, 0x1FAC2}, {0x1FAD0, 0x1FAD6}, {0x20000, 0x2FFFD},
|
|
{0x30000, 0x3FFFD},
|
|
};
|
|
|
|
template <typename T, int N>
|
|
bool inTable(const T (&table)[N], char32_t cp)
|
|
{
|
|
if (cp < table[0].lo || cp > table[N - 1].hi)
|
|
return false;
|
|
int lo = 0;
|
|
int hi = N - 1;
|
|
while (lo <= hi) {
|
|
const int mid = (lo + hi) / 2;
|
|
if (cp < table[mid].lo) {
|
|
hi = mid - 1;
|
|
} else if (cp > table[mid].hi) {
|
|
lo = mid + 1;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
bool isZeroWidth(char32_t cp) { return inTable(kZeroWidth, cp); }
|
|
|
|
bool isWideChar(char32_t cp) { return inTable(kWide, cp); }
|
|
|
|
int charWidth(char32_t cp)
|
|
{
|
|
if (cp == 0 || cp < 0x20 || (cp >= 0x7F && cp < 0xA0))
|
|
return 0;
|
|
if (isZeroWidth(cp))
|
|
return 0;
|
|
if (isWideChar(cp))
|
|
return 2;
|
|
return 1;
|
|
}
|
|
|
|
} // namespace vx
|