File indexing completed on 2024-05-05 05:53:43

0001 /*
0002     SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
0003     SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #ifndef CHARACTER_H
0009 #define CHARACTER_H
0010 
0011 // Konsole
0012 #include "CharacterColor.h"
0013 #include "CharacterWidth.h"
0014 #include "ExtendedCharTable.h"
0015 #include "Hangul.h"
0016 #include "LineBlockCharacters.h"
0017 
0018 // Qt
0019 #include <QVector>
0020 
0021 /* clang-format off */
0022 const int LINE_WRAPPED              = (1 << 0);
0023 const int LINE_DOUBLEWIDTH          = (1 << 1);
0024 const int LINE_DOUBLEHEIGHT_TOP     = (1 << 2);
0025 const int LINE_DOUBLEHEIGHT_BOTTOM  = (1 << 3);
0026 const int LINE_PROMPT_START         = (1 << 4);
0027 const int LINE_INPUT_START          = (1 << 5);
0028 const int LINE_OUTPUT_START         = (1 << 6);
0029 /* clang-format on */
0030 
0031 namespace Konsole
0032 {
0033 #pragma pack(1)
0034 class LineProperty
0035 {
0036 public:
0037     explicit constexpr LineProperty(quint16 f = 0, uint l = 0, uint c = 0)
0038         : flags({f})
0039         , length(l)
0040         , counter(c)
0041     {
0042     }
0043     union {
0044         quint16 all;
0045         struct {
0046             uint wrapped : 1;
0047             uint doublewidth : 1;
0048             uint doubleheight_top : 1;
0049             uint doubleheight_bottom : 1;
0050             uint prompt_start : 1;
0051             uint output_start : 1;
0052             uint input_start : 1;
0053             uint output : 1;
0054             uint error : 1;
0055         } f;
0056     } flags;
0057     qint16 length;
0058     quint16 counter;
0059     bool operator!=(const LineProperty &rhs) const
0060     {
0061         return (flags.all != rhs.flags.all);
0062     }
0063     void resetStarts()
0064     {
0065         flags.all &= ~(LINE_PROMPT_START | LINE_INPUT_START | LINE_OUTPUT_START);
0066     }
0067     quint16 getStarts() const
0068     {
0069         return flags.all & (LINE_PROMPT_START | LINE_INPUT_START | LINE_OUTPUT_START);
0070     }
0071     void setStarts(quint16 starts)
0072     {
0073         flags.all = (flags.all & ~(LINE_PROMPT_START | LINE_INPUT_START | LINE_OUTPUT_START)) | starts;
0074     }
0075 };
0076 
0077 typedef union {
0078     quint16 all;
0079     struct {
0080         uint bold : 1;
0081         uint blink : 1;
0082         uint transparent : 1;
0083         uint reverse : 1;
0084         uint italic : 1;
0085         uint cursor : 1;
0086         uint extended : 1;
0087         uint faint : 1;
0088         uint strikeout : 1;
0089         uint conceal : 1;
0090         uint overline : 1;
0091         uint selected : 1;
0092         uint underline : 4;
0093     } f;
0094 } RenditionFlagsC;
0095 typedef quint16 RenditionFlags;
0096 #pragma pack()
0097 
0098 typedef quint16 ExtraFlags;
0099 
0100 /* clang-format off */
0101 const RenditionFlags DEFAULT_RENDITION  = 0;
0102 const RenditionFlags RE_BOLD            = (1 << 0);
0103 const RenditionFlags RE_BLINK           = (1 << 1);
0104 const RenditionFlags RE_TRANSPARENT     = (1 << 2);
0105 const RenditionFlags RE_REVERSE         = (1 << 3); // Screen only
0106 const RenditionFlags RE_ITALIC          = (1 << 4);
0107 const RenditionFlags RE_CURSOR          = (1 << 5);
0108 const RenditionFlags RE_EXTENDED_CHAR   = (1 << 6);
0109 const RenditionFlags RE_FAINT           = (1 << 7);
0110 const RenditionFlags RE_STRIKEOUT       = (1 << 8);
0111 const RenditionFlags RE_CONCEAL         = (1 << 9);
0112 const RenditionFlags RE_OVERLINE        = (1 << 10);
0113 const RenditionFlags RE_SELECTED        = (1 << 11);
0114 const RenditionFlags RE_UNDERLINE_MASK  = (15 << 12);
0115 const RenditionFlags RE_UNDERLINE_NONE  = 0;
0116 const RenditionFlags RE_UNDERLINE       = 1;
0117 const RenditionFlags RE_UNDERLINE_DOUBLE= 2;
0118 const RenditionFlags RE_UNDERLINE_CURL  = 3;
0119 const RenditionFlags RE_UNDERLINE_DOT   = 4;
0120 const RenditionFlags RE_UNDERLINE_DASH  = 5;
0121 const RenditionFlags RE_UNDERLINE_BIT   = (1 << 12);
0122 // Masks of flags that matter for drawing what is below/above the text
0123 const RenditionFlags RE_MASK_UNDER = RE_TRANSPARENT | RE_REVERSE | RE_CURSOR | RE_SELECTED;
0124 const RenditionFlags RE_MASK_ABOVE = RE_TRANSPARENT | RE_REVERSE | RE_CURSOR | RE_SELECTED | RE_STRIKEOUT | RE_CONCEAL | RE_OVERLINE | RE_UNDERLINE_MASK;
0125 
0126 // flags that affect how the text is drawn
0127 // without RE_REVERSE, since the foreground color is calculated
0128 const RenditionFlags RE_TEXTDRAWING = RE_BOLD | RE_BLINK | RE_TRANSPARENT | RE_ITALIC | RE_CURSOR | RE_FAINT | RE_SELECTED;
0129 
0130 
0131 const ExtraFlags EF_UNREAL       = 0;
0132 const ExtraFlags EF_REAL         = (1 << 0);
0133 const ExtraFlags EF_REPL         = (3 << 1);
0134 const ExtraFlags EF_REPL_NONE    = (0 << 1);
0135 const ExtraFlags EF_REPL_PROMPT  = (1 << 1);
0136 const ExtraFlags EF_REPL_INPUT   = (2 << 1);
0137 const ExtraFlags EF_REPL_OUTPUT  = (3 << 1);
0138 const ExtraFlags EF_UNDERLINE_COLOR = (15 << 3);
0139 const ExtraFlags EF_UNDERLINE_COLOR_1 = (1 << 3);
0140 const ExtraFlags EF_EMOJI_REPRESENTATION = (1 << 7);
0141 const ExtraFlags EF_ASCII_WORD = (1 << 8);
0142 const ExtraFlags EF_BRAHMIC_WORD = (1 << 9);
0143 
0144 #define SetULColor(f, m) (((f) & ~EF_UNDERLINE_COLOR) | ((m) * EF_UNDERLINE_COLOR_1))
0145 #define setRepl(f, m) (((f) & ~EF_REPL) | ((m) * EF_REPL_PROMPT))
0146 /* clang-format on */
0147 
0148 /**
0149  * A single character in the terminal which consists of a unicode character
0150  * value, foreground and background colors and a set of rendition attributes
0151  * which specify how it should be drawn.
0152  */
0153 class Character
0154 {
0155 public:
0156     /**
0157      * Constructs a new character.
0158      *
0159      * @param _c The unicode character value of this character.
0160      * @param _f The foreground color used to draw the character.
0161      * @param _b The color used to draw the character's background.
0162      * @param _r A set of rendition flags which specify how this character
0163      *           is to be drawn.
0164      * @param _flags Extra flags describing the character. not directly related to its rendition
0165      */
0166     explicit constexpr Character(uint _c = ' ',
0167                                  CharacterColor _f = CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR),
0168                                  CharacterColor _b = CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR),
0169                                  RenditionFlags _r = DEFAULT_RENDITION,
0170                                  ExtraFlags _flags = EF_REAL)
0171         : character(_c)
0172         , rendition({_r})
0173         , foregroundColor(_f)
0174         , backgroundColor(_b)
0175         , flags(_flags)
0176     {
0177     }
0178 
0179     /** The unicode character value for this character.
0180      *
0181      * if RE_EXTENDED_CHAR is set, character is a hash code which can be used to
0182      * look up the unicode character sequence in the ExtendedCharTable used to
0183      * create the sequence.
0184      */
0185     char32_t character;
0186 
0187     /** A combination of RENDITION flags which specify options for drawing the character. */
0188     RenditionFlagsC rendition;
0189 
0190     /** The foreground color used to draw this character. */
0191     CharacterColor foregroundColor;
0192 
0193     /** The color used to draw this character's background. */
0194     CharacterColor backgroundColor;
0195 
0196     /** Flags which are not specific to rendition
0197      * Indicate whether this character really exists, or exists simply as place holder.
0198      * REPL mode
0199      * Character type (script, etc.)
0200      */
0201     ExtraFlags flags;
0202 
0203     /**
0204      * returns true if the format (color, rendition flag) of the compared characters is equal
0205      */
0206     constexpr bool equalsFormat(const Character &other) const;
0207 
0208     /**
0209      * Compares two characters and returns true if they have the same unicode character value,
0210      * rendition and colors.
0211      */
0212     friend constexpr bool operator==(const Character &a, const Character &b);
0213 
0214     /**
0215      * Compares two characters and returns true if they have different unicode character values,
0216      * renditions or colors.
0217      */
0218     friend constexpr bool operator!=(const Character &a, const Character &b);
0219 
0220     constexpr bool isSpace() const
0221     {
0222         if (rendition.f.extended) {
0223             return false;
0224         } else {
0225             return QChar::isSpace(character);
0226         }
0227     }
0228 
0229     int width() const
0230     {
0231         return width(character);
0232     }
0233 
0234     int repl() const
0235     {
0236         return flags & EF_REPL;
0237     }
0238 
0239     static constexpr int emojiPresentation1Start = 8986;
0240     static constexpr int emojiPresentation1End = 11093;
0241     static constexpr int emojiPresentation2Start = 126980;
0242     static constexpr int emojiPresentation2End = 129782;
0243     /* clang-format off */
0244     static constexpr uint64_t emojiPresentation1Bits[] = {
0245         0x3, 0x0, 0x0, 0x2478000, 0x0, 0x0, 0x0, 0x0,
0246         0x0, 0x0, 0x0, 0xc00001800000000, 0x3ffc00000000000, 0x200002000000000, 0x4100c1800030080, 0x308090b010000,
0247         0x2e14000000004000, 0x3800000000000000, 0x2000400000, 0x0, 0x0, 0x0, 0x0, 0x0,
0248         0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0249         0x840000000000006
0250     };
0251     static constexpr uint64_t emojiPresentation2Bits[] = {
0252         0x1, 0x0, 0x0, 0x800, 0x0, 0x0, 0x7fe400, 0x2ffffffc00000000,
0253         0x77c80000400000, 0x3000, 0x0, 0xf000000000000000,
0254         0xfffbfe001fffffff, 0xfdffffffffffffff, 0xfffffffff000ffff, 0xfff11ffff000f87f,
0255         0xd7ffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xf9ffffffffffffff,
0256         0x3ffffffffffffff, 0x40000ffffff780, 0x100060000, 0xff80000000000000,
0257         0xffffffffffffffff, 0xf000000000000fff, 0xffffffffffffffff, 0x1ff01800e0e7103, 0x0, 0x0, 0x0, 0x10fff0000000,
0258         0x0, 0x0, 0x0, 0x0, 0xff7fffffffffff00, 0xfffffffffffffffb, 0xffffffffffffffff, 0xfffffffffffffff,
0259         0x0, 0xf1f1f00000000000, 0xf07ff1fffffff007, 0x7f00ff03ff003
0260     };
0261     /* clang-format on */
0262 
0263     static bool emojiPresentation(uint ucs4)
0264     {
0265         if (ucs4 >= emojiPresentation1Start && ucs4 <= emojiPresentation1End) {
0266             return (emojiPresentation1Bits[(ucs4 - emojiPresentation1Start) / 64] & (uint64_t(1) << ((ucs4 - emojiPresentation1Start) % 64))) != 0;
0267         } else if (ucs4 >= emojiPresentation2Start && ucs4 <= emojiPresentation2End) {
0268             return (emojiPresentation2Bits[(ucs4 - emojiPresentation2Start) / 64] & (uint64_t(1) << ((ucs4 - emojiPresentation2Start) % 64))) != 0;
0269         }
0270         return false;
0271     }
0272 
0273     static constexpr int emoji1Start = 8252;
0274     static constexpr int emoji1End = 12953;
0275     static constexpr int emoji2Start = 126980;
0276     static constexpr int emoji2End = 129782;
0277     /* clang-format off */
0278     static constexpr uint64_t emoji1Bits[] = {
0279         0x2001, 0x0, 0x0, 0x2000004000000000, 0x0, 0x60003f000000, 0x0, 0x0,
0280         0x0, 0x0, 0x0, 0x1000c0000000, 0x0, 0x0, 0x70ffe00000080000, 0x0,
0281         0x0, 0x0, 0x40, 0x0, 0x0, 0x400c00000000000, 0x8000000000000010, 0x700c44d2132401f7,
0282         0x8000169800fff050, 0x30c831afc0000c, 0x7bf0600001ac1306, 0x1801022054bf242, 0x1800b850900, 0x1000200e000000, 0x8, 0x0,
0283         0x0, 0x0, 0x0, 0x300000000000000, 0x0, 0x0, 0x0, 0x0,
0284         0x0, 0x0, 0x0, 0x180000e00, 0x2100000, 0x0, 0x0, 0x0,
0285         0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0286         0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10000000000000,
0287         0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0288         0x0, 0x28000000
0289     };
0290     static constexpr uint64_t emoji2Bits[] = {
0291         0x1, 0x0, 0x0, 0x800, 0x0, 0xc00300000000000, 0x7fe400, 0x6ffffffc00000000,
0292         0x7fc80000400000, 0x3000, 0x0, 0xf000000000000000,
0293         0xffffffff3fffffff, 0xffffffffffffffff, 0xfffffffffcecffff, 0xfffb9fffffffffff,
0294         0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfbffffffffffffff,
0295         0x3ffffffffffffff, 0x7f980ffffff7e0, 0xc1006013000613c8, 0xffc08810a700e001,
0296         0xffffffffffffffff, 0xf000000000000fff, 0xffffffffffffffff, 0x1ff91a3fe0e7f83, 0x0, 0x0, 0x0, 0x10fff0000000,
0297         0x0, 0x0, 0x0, 0x0, 0xff7fffffffffff00, 0xfffffffffffffffb, 0xffffffffffffffff, 0xfffffffffffffff,
0298         0x0, 0xf1f1f00000000000, 0xf07ff1fffffff007, 0x7f00ff03ff003
0299     };
0300     /* clang-format on */
0301 
0302     static bool emoji(uint ucs4)
0303     {
0304         if (ucs4 >= emoji1Start && ucs4 <= emoji1End) {
0305             return (emoji1Bits[(ucs4 - emoji1Start) / 64] & (uint64_t(1) << ((ucs4 - emoji1Start) % 64))) != 0;
0306         } else if (ucs4 >= emoji2Start && ucs4 <= emoji2End) {
0307             return (emoji2Bits[(ucs4 - emoji2Start) / 64] & (uint64_t(1) << ((ucs4 - emoji2Start) % 64))) != 0;
0308         }
0309         return false;
0310     }
0311 
0312     static int width(uint ucs4)
0313     {
0314         // ASCII
0315         if (ucs4 >= 0x20 && ucs4 < 0x7f)
0316             return 1;
0317 
0318         if (ucs4 >= 0xA0 && ucs4 <= 0xFF)
0319             return 1;
0320 
0321         // NULL
0322         if (ucs4 == 0)
0323             return 0;
0324 
0325         // Control chars
0326         if ((ucs4 > 0x0 && ucs4 < 0x20) || (ucs4 >= 0x7F && ucs4 < 0xA0))
0327             return -1;
0328 
0329         return characterWidth(ucs4);
0330     }
0331 
0332     static int stringWidth(const char32_t *ucs4Str, int len)
0333     {
0334         int w = 0;
0335         Hangul::SyllablePos hangulSyllablePos = Hangul::NotInSyllable;
0336 
0337         for (int i = 0; i < len; ++i) {
0338             const uint c = ucs4Str[i];
0339 
0340             if (!Hangul::isHangul(c)) {
0341                 w += width(c);
0342                 hangulSyllablePos = Hangul::NotInSyllable;
0343             } else {
0344                 w += Hangul::width(c, width(c), hangulSyllablePos);
0345             }
0346         }
0347         return w;
0348     }
0349 
0350     inline static int stringWidth(const QString &str)
0351     {
0352         const auto ucs4Str = str.toStdU32String();
0353         return stringWidth(ucs4Str.data(), ucs4Str.size());
0354     }
0355 
0356     inline uint baseCodePoint() const
0357     {
0358         if (rendition.f.extended) {
0359             ushort extendedCharLength = 0;
0360             const char32_t *chars = ExtendedCharTable::instance.lookupExtendedChar(character, extendedCharLength);
0361             // FIXME: Coverity-Dereferencing chars, which is known to be nullptr
0362             return chars[0];
0363         }
0364         return character;
0365     }
0366 
0367     inline bool isSameScript(Character lhs) const
0368     {
0369         const QChar::Script script = QChar::script(lhs.baseCodePoint());
0370         const QChar::Script currentScript = QChar::script(baseCodePoint());
0371         if (currentScript == QChar::Script_Common || script == QChar::Script_Common || currentScript == QChar::Script_Inherited
0372             || script == QChar::Script_Inherited) {
0373             return true;
0374         }
0375         return currentScript == script;
0376     }
0377 
0378     inline bool hasSameColors(Character lhs) const
0379     {
0380         return lhs.foregroundColor == foregroundColor && lhs.backgroundColor == backgroundColor;
0381     }
0382 
0383     inline bool hasSameRendition(Character lhs) const
0384     {
0385         return (lhs.rendition.all & ~RE_EXTENDED_CHAR) == (rendition.all & ~RE_EXTENDED_CHAR) && lhs.flags == flags;
0386     }
0387 
0388     inline bool hasSameLineDrawStatus(Character lhs) const
0389     {
0390         const bool lineDraw = LineBlockCharacters::canDraw(character);
0391         return LineBlockCharacters::canDraw(lhs.character) == lineDraw;
0392     }
0393 
0394     inline bool hasSameAttributes(Character lhs) const
0395     {
0396         return hasSameColors(lhs) && hasSameRendition(lhs) && hasSameLineDrawStatus(lhs) && isSameScript(lhs);
0397     }
0398 
0399     inline bool notSameAttributesText(Character lhs) const
0400     {
0401         // Only compare attributes used for drawing text
0402         return (lhs.rendition.all & RE_TEXTDRAWING) != (rendition.all & RE_TEXTDRAWING) || lhs.foregroundColor != foregroundColor;
0403     }
0404 
0405     inline bool isRightHalfOfDoubleWide() const
0406     {
0407         return character == 0;
0408     }
0409 
0410     inline void setRightHalfOfDoubleWide()
0411     {
0412         character = 0;
0413     }
0414 };
0415 
0416 constexpr bool operator==(const Character &a, const Character &b)
0417 {
0418     return a.character == b.character && a.equalsFormat(b);
0419 }
0420 
0421 constexpr bool operator!=(const Character &a, const Character &b)
0422 {
0423     return !operator==(a, b);
0424 }
0425 
0426 constexpr bool Character::equalsFormat(const Character &other) const
0427 {
0428     return backgroundColor == other.backgroundColor && foregroundColor == other.foregroundColor && rendition.all == other.rendition.all;
0429 }
0430 }
0431 Q_DECLARE_TYPEINFO(Konsole::Character, Q_MOVABLE_TYPE);
0432 
0433 #endif // CHARACTER_H