diff --git a/lib/Character.h b/lib/Character.h index 536cd63..0777a7e 100644 --- a/lib/Character.h +++ b/lib/Character.h @@ -48,6 +48,10 @@ static const int LINE_DOUBLEHEIGHT = (1 << 2); #define RE_ITALIC (1 << 4) #define RE_CURSOR (1 << 5) #define RE_EXTENDED_CHAR (1 << 6) +#define RE_FAINT (1 << 7) +#define RE_STRIKEOUT (1 << 8) +#define RE_CONCEAL (1 << 9) +#define RE_OVERLINE (1 << 10) /** * A single character in the terminal which consists of a unicode character diff --git a/lib/KeyboardTranslator.cpp b/lib/KeyboardTranslator.cpp index 70b5b44..68657a7 100644 --- a/lib/KeyboardTranslator.cpp +++ b/lib/KeyboardTranslator.cpp @@ -321,6 +321,10 @@ bool KeyboardTranslatorReader::parseAsCommand(const QString& text,KeyboardTransl command = KeyboardTranslator::ScrollLineDownCommand; else if ( text.compare("scrolllock",Qt::CaseInsensitive) == 0 ) command = KeyboardTranslator::ScrollLockCommand; + else if ( text.compare("scrolluptotop",Qt::CaseInsensitive) == 0) + command = KeyboardTranslator::ScrollUpToTopCommand; + else if ( text.compare("scrolldowntobottom",Qt::CaseInsensitive) == 0) + command = KeyboardTranslator::ScrollDownToBottomCommand; else return false; @@ -785,6 +789,10 @@ QString KeyboardTranslator::Entry::resultToString(bool expandWildCards,Qt::Keybo return "ScrollLineDown"; else if ( _command == ScrollLockCommand ) return "ScrollLock"; + else if (_command == ScrollUpToTopCommand) + return "ScrollUpToTop"; + else if (_command == ScrollDownToBottomCommand) + return "ScrollDownToBottom"; return QString(); } diff --git a/lib/KeyboardTranslator.h b/lib/KeyboardTranslator.h index 37efc10..a9614e4 100644 --- a/lib/KeyboardTranslator.h +++ b/lib/KeyboardTranslator.h @@ -111,8 +111,12 @@ public: ScrollLineDownCommand = 16, /** Toggles scroll lock mode */ ScrollLockCommand = 32, + /** Scroll the terminal display up to the start of history */ + ScrollUpToTopCommand = 64, + /** Scroll the terminal display down to the end of history */ + ScrollDownToBottomCommand = 128, /** Echos the operating system specific erase character. */ - EraseCommand = 64 + EraseCommand = 256 }; Q_DECLARE_FLAGS(Commands,Command) diff --git a/lib/TerminalDisplay.cpp b/lib/TerminalDisplay.cpp index 768e33f..ce27706 100644 --- a/lib/TerminalDisplay.cpp +++ b/lib/TerminalDisplay.cpp @@ -783,23 +783,30 @@ void TerminalDisplay::drawCharacters(QPainter& painter, { // don't draw text which is currently blinking if ( _blinking && (style->rendition & RE_BLINK) ) + return; + + // don't draw concealed characters + if (style->rendition & RE_CONCEAL) return; // setup bold and underline - bool useBold; - ColorEntry::FontWeight weight = style->fontWeight(_colorTable); - if (weight == ColorEntry::UseCurrentFormat) - useBold = ((style->rendition & RE_BOLD) && _boldIntense) || font().bold(); - else - useBold = (weight == ColorEntry::Bold) ? true : false; - bool useUnderline = style->rendition & RE_UNDERLINE || font().underline(); + bool useBold = ((style->rendition & RE_BOLD) && _boldIntense) || font().bold(); + const bool useUnderline = style->rendition & RE_UNDERLINE || font().underline(); + const bool useItalic = style->rendition & RE_ITALIC || font().italic(); + const bool useStrikeOut = style->rendition & RE_STRIKEOUT || font().strikeOut(); + const bool useOverline = style->rendition & RE_OVERLINE || font().overline(); QFont font = painter.font(); if ( font.bold() != useBold - || font.underline() != useUnderline ) - { + || font.underline() != useUnderline + || font.italic() != useItalic + || font.strikeOut() != useStrikeOut + || font.overline() != useOverline) { font.setBold(useBold); font.setUnderline(useUnderline); + font.setItalic(useItalic); + font.setStrikeOut(useStrikeOut); + font.setOverline(useOverline); painter.setFont(font); } @@ -818,16 +825,17 @@ void TerminalDisplay::drawCharacters(QPainter& painter, drawLineCharString(painter,rect.x(),rect.y(),text,style); else { - // the drawText(rect,flags,string) overload is used here with null flags - // instead of drawText(rect,string) because the (rect,string) overload causes - // the application's default layout direction to be used instead of - // the widget-specific layout direction, which should always be - // Qt::LeftToRight for this widget - // This was discussed in: http://lists.kde.org/?t=120552223600002&r=1&w=2 - if (_bidiEnabled) - painter.drawText(rect,0,text); - else - painter.drawText(rect, Qt::AlignBottom, LTR_OVERRIDE_CHAR + text); + // Force using LTR as the document layout for the terminal area, because + // there is no use cases for RTL emulator and RTL terminal application. + // + // This still allows RTL characters to be rendered in the RTL way. + painter.setLayoutDirection(Qt::LeftToRight); + + if (_bidiEnabled) { + painter.drawText(rect.x(), rect.y() + _fontAscent + _lineSpacing, text); + } else { + painter.drawText(rect.x(), rect.y() + _fontAscent + _lineSpacing, LTR_OVERRIDE_CHAR + text); + } } } diff --git a/lib/Vt102Emulation.cpp b/lib/Vt102Emulation.cpp index 7a0587f..6f6d494 100644 --- a/lib/Vt102Emulation.cpp +++ b/lib/Vt102Emulation.cpp @@ -133,6 +133,7 @@ void Vt102Emulation::reset() - ESC_DE - Escape codes of the form C - CSI_PN - Escape codes of the form '[' {Pn} ';' {Pn} C - CSI_PS - Escape codes of the form '[' {Pn} ';' ... C + - CSI_PS_SP - Escape codes of the form '[' {Pn} ';' ... {Space} C - CSI_PR - Escape codes of the form '[' '?' {Pn} ';' ... C - CSI_PE - Escape codes of the form '[' '!' {Pn} ';' ... C - VT52 - VT52 escape codes @@ -160,6 +161,7 @@ void Vt102Emulation::reset() #define TY_CSI_PS(A,N) TY_CONSTRUCT(5,A,N) #define TY_CSI_PN(A ) TY_CONSTRUCT(6,A,0) #define TY_CSI_PR(A,N) TY_CONSTRUCT(7,A,N) +#define TY_CSI_PS_SP(A,N) TY_CONSTRUCT(11,A,N) #define TY_VT52(A) TY_CONSTRUCT(8,A,0) #define TY_CSI_PG(A) TY_CONSTRUCT(9,A,0) @@ -203,7 +205,6 @@ void Vt102Emulation::addToCurrentToken(int cc) } // Character Class flags used while decoding - #define CTL 1 // Control character #define CHR 2 // Printable character #define CPN 4 // TODO: Document me @@ -266,17 +267,19 @@ void Vt102Emulation::initTokenizer() #define epp( ) (p >= 3 && s[2] == '?') #define epe( ) (p >= 3 && s[2] == '!') #define egt( ) (p >= 3 && s[2] == '>') +#define esp( ) (p == 4 && s[3] == ' ') #define Xpe (tokenBufferPos >= 2 && tokenBuffer[1] == ']') -#define Xte (Xpe && cc == 7 ) +#define Xte (Xpe && (cc == 7 || cc == 33)) #define ces(C) (cc < 256 && (charClass[cc] & (C)) == (C) && !Xte) -#define ESC 27 #define CNTL(c) ((c)-'@') +#define ESC 27 +#define DEL 127 // process an incoming unicode character void Vt102Emulation::receiveChar(int cc) { - if (cc == 127) + if (cc == DEL) return; //VT100: ignore. if (ces(CTL)) @@ -314,6 +317,12 @@ void Vt102Emulation::receiveChar(int cc) if (les(3,1,SCS)) { processToken( TY_ESC_CS(s[1],s[2]), 0, 0); resetTokenizer(); return; } if (lec(3,1,'#')) { processToken( TY_ESC_DE(s[2]), 0, 0); resetTokenizer(); return; } if (eps( CPN)) { processToken( TY_CSI_PN(cc), argv[0],argv[1]); resetTokenizer(); return; } + if (esp( )) { return; } + if (lec(5, 4, 'q') && s[3] == ' ') { + processToken( TY_CSI_PS_SP(cc, argv[0]), argv[0], 0); + resetTokenizer(); + return; + } // resize = \e[8;;t if (eps(CPS)) @@ -531,7 +540,7 @@ void Vt102Emulation::processToken(int token, int p, int q) case TY_ESC_DE('8' ) : _currentScreen->helpAlign ( ); break; // resize = \e[8;;t - case TY_CSI_PS('t', 8) : setImageSize( q /* columns */, p /* lines */ ); + case TY_CSI_PS('t', 8) : setImageSize( p /*lines */, q /* columns */ ); emit imageResizeRequest(QSize(q, p)); break; @@ -557,18 +566,27 @@ void Vt102Emulation::processToken(int token, int p, int q) case TY_CSI_PS('m', 0) : _currentScreen->setDefaultRendition ( ); break; case TY_CSI_PS('m', 1) : _currentScreen-> setRendition (RE_BOLD ); break; //VT100 + case TY_CSI_PS('m', 2) : _currentScreen-> setRendition (RE_FAINT ); break; case TY_CSI_PS('m', 3) : _currentScreen-> setRendition (RE_ITALIC ); break; //VT100 case TY_CSI_PS('m', 4) : _currentScreen-> setRendition (RE_UNDERLINE); break; //VT100 case TY_CSI_PS('m', 5) : _currentScreen-> setRendition (RE_BLINK ); break; //VT100 case TY_CSI_PS('m', 7) : _currentScreen-> setRendition (RE_REVERSE ); break; + case TY_CSI_PS('m', 8) : _currentScreen-> setRendition (RE_CONCEAL ); break; + case TY_CSI_PS('m', 9) : _currentScreen-> setRendition (RE_STRIKEOUT); break; + case TY_CSI_PS('m', 53) : _currentScreen-> setRendition (RE_OVERLINE ); break; case TY_CSI_PS('m', 10) : /* IGNORED: mapping related */ break; //LINUX case TY_CSI_PS('m', 11) : /* IGNORED: mapping related */ break; //LINUX case TY_CSI_PS('m', 12) : /* IGNORED: mapping related */ break; //LINUX - case TY_CSI_PS('m', 22) : _currentScreen->resetRendition (RE_BOLD ); break; + case TY_CSI_PS('m', 21) : _currentScreen->resetRendition (RE_BOLD ); break; + case TY_CSI_PS('m', 22) : _currentScreen->resetRendition (RE_BOLD ); + _currentScreen->resetRendition (RE_FAINT ); break; case TY_CSI_PS('m', 23) : _currentScreen->resetRendition (RE_ITALIC ); break; //VT100 case TY_CSI_PS('m', 24) : _currentScreen->resetRendition (RE_UNDERLINE); break; case TY_CSI_PS('m', 25) : _currentScreen->resetRendition (RE_BLINK ); break; case TY_CSI_PS('m', 27) : _currentScreen->resetRendition (RE_REVERSE ); break; + case TY_CSI_PS('m', 28) : _currentScreen->resetRendition (RE_CONCEAL ); break; + case TY_CSI_PS('m', 29) : _currentScreen->resetRendition (RE_STRIKEOUT); break; + case TY_CSI_PS('m', 55) : _currentScreen->resetRendition (RE_OVERLINE ); break; case TY_CSI_PS('m', 30) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 0); break; case TY_CSI_PS('m', 31) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 1); break; @@ -624,6 +642,14 @@ void Vt102Emulation::processToken(int token, int p, int q) case TY_CSI_PS('x', 0) : reportTerminalParms ( 2); break; //VT100 case TY_CSI_PS('x', 1) : reportTerminalParms ( 3); break; //VT100 + case TY_CSI_PS_SP('q', 0) : emit titleChanged( 50, "CursorShape=0;BlinkingCursorEnabled=1" ); break; + case TY_CSI_PS_SP('q', 1) : emit titleChanged( 50, "CursorShape=0;BlinkingCursorEnabled=1" ); break; + case TY_CSI_PS_SP('q', 2) : emit titleChanged( 50, "CursorShape=0;BlinkingCursorEnabled=0" ); break; + case TY_CSI_PS_SP('q', 3) : emit titleChanged( 50, "CursorShape=1;BlinkingCursorEnabled=1" ); break; + case TY_CSI_PS_SP('q', 4) : emit titleChanged( 50, "CursorShape=1;BlinkingCursorEnabled=0" ); break; + case TY_CSI_PS_SP('q', 5) : emit titleChanged( 50, "CursorShape=2;BlinkingCursorEnabled=1" ); break; + case TY_CSI_PS_SP('q', 6) : emit titleChanged( 50, "CursorShape=2;BlinkingCursorEnabled=0" ); break; + case TY_CSI_PN('@' ) : _currentScreen->insertChars (p ); break; case TY_CSI_PN('A' ) : _currentScreen->cursorUp (p ); break; //VT100 case TY_CSI_PN('B' ) : _currentScreen->cursorDown (p ); break; //VT100 @@ -885,12 +911,12 @@ void Vt102Emulation::reportAnswerBack() /*! `cx',`cy' are 1-based. - `eventType' indicates the button pressed (0-2) - or a general mouse release (3). + `cb' indicates the button pressed or released (0-2) or scroll event (4-5). eventType represents the kind of mouse action that occurred: - 0 = Mouse button press or release + 0 = Mouse button press 1 = Mouse drag + 2 = Mouse button release */ void Vt102Emulation::sendMouseEvent( int cb, int cx, int cy , int eventType ) @@ -905,10 +931,31 @@ void Vt102Emulation::sendMouseEvent( int cb, int cx, int cy , int eventType ) //Mouse motion handling if ((getMode(MODE_Mouse1002) || getMode(MODE_Mouse1003)) && eventType == 1) - cb += 0x20; //add 32 to signify motion event + cb += 0x20; //add 32 to signify motion event + + char command[32]; + command[0] = '\0'; + // Check the extensions in decreasing order of preference. Encoding the release event above assumes that 1006 comes first. + if (getMode(MODE_Mouse1006)) { + snprintf(command, sizeof(command), "\033[<%d;%d;%d%c", cb, cx, cy, eventType == 2 ? 'm' : 'M'); + } else if (getMode(MODE_Mouse1015)) { + snprintf(command, sizeof(command), "\033[%d;%d;%dM", cb + 0x20, cx, cy); + } else if (getMode(MODE_Mouse1005)) { + if (cx <= 2015 && cy <= 2015) { + // The xterm extension uses UTF-8 (up to 2 bytes) to encode + // coordinate+32, no matter what the locale is. We could easily + // convert manually, but QString can also do it for us. + QChar coords[2]; + coords[0] = cx + 0x20; + coords[1] = cy + 0x20; + QString coordsStr = QString(coords, 2); + QByteArray utf8 = coordsStr.toUtf8(); + snprintf(command, sizeof(command), "\033[M%c%s", cb + 0x20, utf8.constData()); + } + } else if (cx <= 223 && cy <= 223) { + snprintf(command, sizeof(command), "\033[M%c%c%c", cb + 0x20, cx + 0x20, cy + 0x20); + } - char command[20]; - sprintf(command,"\033[M%c%c%c",cb+0x20,cx+0x20,cy+0x20); sendString(command); } @@ -965,10 +1012,15 @@ void Vt102Emulation::sendKeyEvent( QKeyEvent* event ) // check flow control state if (modifiers & Qt::ControlModifier) { - if (event->key() == Qt::Key_S) + switch (event->key()) { + case Qt::Key_S: emit flowControlKeyPressed(true); - else if (event->key() == Qt::Key_Q) + break; + case Qt::Key_Q: + case Qt::Key_C: // cancel flow control emit flowControlKeyPressed(false); + break; + } } // lookup key binding @@ -987,6 +1039,7 @@ void Vt102Emulation::sendKeyEvent( QKeyEvent* event ) // (unless there is an entry defined for this particular combination // in the keyboard modifier) bool wantsAltModifier = entry.modifiers() & entry.modifierMask() & Qt::AltModifier; + bool wantsMetaModifier = entry.modifiers() & entry.modifierMask() & Qt::MetaModifier; bool wantsAnyModifier = entry.state() & entry.stateMask() & KeyboardTranslator::AnyModifierState; @@ -995,6 +1048,11 @@ void Vt102Emulation::sendKeyEvent( QKeyEvent* event ) { textToSend.prepend("\033"); } + if ( modifiers & Qt::MetaModifier && !(wantsMetaModifier || wantsAnyModifier) + && !event->text().isEmpty() ) + { + textToSend.prepend("\030@s"); + } if ( entry.command() != KeyboardTranslator::NoCommand ) { @@ -1169,6 +1227,9 @@ void Vt102Emulation::resetModes() resetMode(MODE_Mouse1001); saveMode(MODE_Mouse1001); resetMode(MODE_Mouse1002); saveMode(MODE_Mouse1002); resetMode(MODE_Mouse1003); saveMode(MODE_Mouse1003); + resetMode(MODE_Mouse1005); saveMode(MODE_Mouse1005); + resetMode(MODE_Mouse1006); saveMode(MODE_Mouse1006); + resetMode(MODE_Mouse1015); saveMode(MODE_Mouse1015); resetMode(MODE_BracketedPaste); saveMode(MODE_BracketedPaste); resetMode(MODE_AppScreen); saveMode(MODE_AppScreen); diff --git a/lib/Vt102Emulation.h b/lib/Vt102Emulation.h index af07675..22a9104 100644 --- a/lib/Vt102Emulation.h +++ b/lib/Vt102Emulation.h @@ -133,7 +133,7 @@ private: void resetModes(); void resetTokenizer(); - #define MAX_TOKEN_LENGTH 80 + #define MAX_TOKEN_LENGTH 256 // Max length of tokens (e.g. window title) void addToCurrentToken(int cc); int tokenBuffer[MAX_TOKEN_LENGTH]; //FIXME: overflow? int tokenBufferPos; @@ -153,6 +153,7 @@ private: void processToken(int code, int p, int q); void processWindowAttributeChange(); + void requestWindowAttribute(int); void reportTerminalType(); void reportSecondaryAttributes();