From e9ee4c9091a91c19ee24a4f8597fa6aa8cb54121 Mon Sep 17 00:00:00 2001 From: Filippo Scognamiglio Date: Thu, 6 Nov 2014 01:45:17 +0100 Subject: [PATCH] Actual QML port, moved from QWidget to QQuickPaintedItem. Setup of the project, added sample test-app. --- lib.pri | 57 ++++++ lib/ScreenWindow.cpp | 2 +- lib/Session.cpp | 16 +- lib/TerminalDisplay.cpp | 364 +++++++++++++++++++++++++---------- lib/TerminalDisplay.h | 60 +++++- lib/tools.cpp | 4 +- qmltermwidget.pro | 26 +++ src/ksession.cpp | 248 ++++++++++++++++++++++++ src/ksession.h | 142 ++++++++++++++ src/qmldir | 3 + src/qmltermwidget_plugin.cpp | 38 ++++ src/qmltermwidget_plugin.h | 17 ++ test-app/test-app.qml | 32 +++ 13 files changed, 890 insertions(+), 119 deletions(-) create mode 100644 lib.pri create mode 100644 qmltermwidget.pro create mode 100644 src/ksession.cpp create mode 100644 src/ksession.h create mode 100644 src/qmldir create mode 100644 src/qmltermwidget_plugin.cpp create mode 100644 src/qmltermwidget_plugin.h create mode 100644 test-app/test-app.qml diff --git a/lib.pri b/lib.pri new file mode 100644 index 0000000..cbff518 --- /dev/null +++ b/lib.pri @@ -0,0 +1,57 @@ +HEADERS += $$PWD/lib/BlockArray.h \ + $$PWD/lib/CharacterColor.h \ + $$PWD/lib/Character.h \ + $$PWD/lib/ColorScheme.h \ + $$PWD/lib/ColorTables.h \ + $$PWD/lib/DefaultTranslatorText.h \ + $$PWD/lib/Emulation.h \ + $$PWD/lib/ExtendedDefaultTranslator.h \ + $$PWD/lib/Filter.h \ + $$PWD/lib/History.h \ + $$PWD/lib/HistorySearch.h \ + $$PWD/lib/KeyboardTranslator.h \ + $$PWD/lib/konsole_wcwidth.h \ + $$PWD/lib/kprocess.h \ + $$PWD/lib/kptydevice.h \ + $$PWD/lib/kpty.h \ + $$PWD/lib/kpty_p.h \ + $$PWD/lib/kptyprocess.h \ + $$PWD/lib/LineFont.h \ + $$PWD/lib/Pty.h \ + $$PWD/lib/Screen.h \ + $$PWD/lib/ScreenWindow.h \ + #$$PWD/lib/SearchBar.h \ + $$PWD/lib/Session.h \ + $$PWD/lib/ShellCommand.h \ + $$PWD/lib/TerminalCharacterDecoder.h \ + $$PWD/lib/TerminalDisplay.h \ + $$PWD/lib/tools.h \ + $$PWD/lib/Vt102Emulation.h \ + #$$PWD/lib/qtermwidget.h + +SOURCES += $$PWD/lib/BlockArray.cpp \ + $$PWD/lib/ColorScheme.cpp \ + $$PWD/lib/Emulation.cpp \ + $$PWD/lib/Filter.cpp \ + $$PWD/lib/History.cpp \ + $$PWD/lib/HistorySearch.cpp \ + $$PWD/lib/KeyboardTranslator.cpp \ + $$PWD/lib/konsole_wcwidth.cpp \ + $$PWD/lib/kprocess.cpp \ + $$PWD/lib/kpty.cpp \ + $$PWD/lib/kptydevice.cpp \ + $$PWD/lib/kptyprocess.cpp \ + $$PWD/lib/Pty.cpp \ + #$$PWD/lib/qtermwidget.cpp \ + $$PWD/lib/Screen.cpp \ + $$PWD/lib/ScreenWindow.cpp \ + #$$PWD/lib/SearchBar.cpp \ + $$PWD/lib/Session.cpp \ + $$PWD/lib/ShellCommand.cpp \ + $$PWD/lib/TerminalCharacterDecoder.cpp \ + $$PWD/lib/TerminalDisplay.cpp \ + $$PWD/lib/tools.cpp \ + $$PWD/lib/Vt102Emulation.cpp + +#FORMS = $$PWD/lib/SearchBar.ui + diff --git a/lib/ScreenWindow.cpp b/lib/ScreenWindow.cpp index 1d3fadd..390ae3f 100644 --- a/lib/ScreenWindow.cpp +++ b/lib/ScreenWindow.cpp @@ -69,7 +69,7 @@ Character* ScreenWindow::getImage() if (!_bufferNeedsUpdate) return _windowBuffer; - + _screen->getImage(_windowBuffer,size, currentLine(),endWindowLine()); diff --git a/lib/Session.cpp b/lib/Session.cpp index e6eca88..9c0d2a1 100644 --- a/lib/Session.cpp +++ b/lib/Session.cpp @@ -45,6 +45,9 @@ #include "ShellCommand.h" #include "Vt102Emulation.h" +// QMLTermWidget +#include + using namespace Konsole; int Session::lastSessionId = 0; @@ -133,15 +136,8 @@ WId Session::windowId() const if ( _views.count() == 0 ) { return 0; } else { - QWidget * window = _views.first(); - - Q_ASSERT( window ); - - while ( window->parentWidget() != 0 ) { - window = window->parentWidget(); - } - - return window->winId(); + QQuickWindow * window = _views.first()->window(); + return (window ? window->winId() : 0); } } @@ -510,7 +506,7 @@ void Session::updateTerminalSize() //select largest number of lines and columns that will fit in all visible views while ( viewIter.hasNext() ) { TerminalDisplay * view = viewIter.next(); - if ( view->isHidden() == false && + if ( !view->isVisible() == false && view->lines() >= VIEW_LINES_THRESHOLD && view->columns() >= VIEW_COLUMNS_THRESHOLD ) { minLines = (minLines == -1) ? view->lines() : qMin( minLines , view->lines() ); diff --git a/lib/TerminalDisplay.cpp b/lib/TerminalDisplay.cpp index c5997c7..1d77d2a 100644 --- a/lib/TerminalDisplay.cpp +++ b/lib/TerminalDisplay.cpp @@ -100,7 +100,7 @@ const ColorEntry Konsole::base_color_table[TABLE_COLORS] = // static bool TerminalDisplay::_antialiasText = true; -bool TerminalDisplay::HAVE_TRANSPARENCY = true; +bool TerminalDisplay::HAVE_TRANSPARENCY = false; // we use this to force QPainter to display text in LTR mode // more information can be found in: http://unicode.org/reports/tr9/ @@ -138,11 +138,12 @@ void TerminalDisplay::setScreenWindow(ScreenWindow* window) { // TODO: Determine if this is an issue. +// TODO: Those two connections caused an additional painting. Determine if this is an issue. //#warning "The order here is not specified - does it matter whether updateImage or updateLineProperties comes first?" connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateLineProperties()) ); connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateImage()) ); - connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateFilters()) ); - connect( _screenWindow , SIGNAL(scrolled(int)) , this , SLOT(updateFilters()) ); + //connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateFilters()) ); + //connect( _screenWindow , SIGNAL(scrolled(int)) , this , SLOT(updateFilters()) ); window->setWindowLines(_lines); } } @@ -263,8 +264,10 @@ void TerminalDisplay::setVTFont(const QFont& f) qDebug() << "Using an unsupported variable-width font in the terminal. This may produce display errors."; } - if ( metrics.height() < height() && metrics.maxWidth() < width() ) - { + //TODO This if has been removed it would be better to understand if this is useful. + //if(font.pixelSize() > 0) { +// if ( metrics.height() < height() && metrics.maxWidth() < width() ) +// { // hint that text should be drawn without anti-aliasing. // depending on the user's font configuration, this may not be respected if (!_antialiasText) @@ -275,13 +278,14 @@ void TerminalDisplay::setVTFont(const QFont& f) // Disabling kerning saves some computation when rendering text. font.setKerning(false); - QWidget::setFont(font); + m_font = font; fontChange(font); - } + //} } -void TerminalDisplay::setFont(const QFont &) +void TerminalDisplay::setFont(const QFont &f) { + Q_UNUSED(f); // ignore font change request if not coming from konsole itself } @@ -291,8 +295,8 @@ void TerminalDisplay::setFont(const QFont &) /* */ /* ------------------------------------------------------------------------- */ -TerminalDisplay::TerminalDisplay(QWidget *parent) -:QWidget(parent) +TerminalDisplay::TerminalDisplay(QQuickItem *parent) +:QQuickPaintedItem(parent) ,_screenWindow(0) ,_allowBell(true) ,_gridLayout(0) @@ -339,10 +343,12 @@ TerminalDisplay::TerminalDisplay(QWidget *parent) ,_filterChain(new TerminalImageFilterChain()) ,_cursorShape(BlockCursor) ,mMotionAfterPasting(NoMoveScreenWindow) +,m_font("Monospace", 12) +,m_color_role(QPalette::Background) { // terminal applications are not designed with Right-To-Left in mind, // so the layout is forced to Left-To-Right - setLayoutDirection(Qt::LeftToRight); + //setLayoutDirection(Qt::LeftToRight); // The offsets are not yet calculated. // Do not calculate these too often to be more smoothly when resizing @@ -350,9 +356,13 @@ TerminalDisplay::TerminalDisplay(QWidget *parent) _topMargin = DEFAULT_TOP_MARGIN; _leftMargin = DEFAULT_LEFT_MARGIN; + m_palette = qApp->palette(); + + setVTFont(m_font); + // create scroll bar for scrolling output up and down // set the scroll bar's slider to occupy the whole area of the scroll bar initially - _scrollBar = new QScrollBar(this); + _scrollBar = new QScrollBar(); setScroll(0,0); _scrollBar->setCursor( Qt::ArrowCursor ); connect(_scrollBar, SIGNAL(valueChanged(int)), this, @@ -371,27 +381,31 @@ TerminalDisplay::TerminalDisplay(QWidget *parent) setUsesMouse(true); setColorTable(base_color_table); - setMouseTracking(true); + //setMouseTracking(true); + + setAcceptedMouseButtons(Qt::LeftButton); + + setFlags(ItemHasContents | ItemAcceptsInputMethod); // Enable drag and drop - setAcceptDrops(true); // attempt - dragInfo.state = diNone; +// setAcceptDrops(true); // attempt +// dragInfo.state = diNone; - setFocusPolicy( Qt::WheelFocus ); +// setFocusPolicy( Qt::WheelFocus ); // enable input method support - setAttribute(Qt::WA_InputMethodEnabled, true); +// setAttribute(Qt::WA_InputMethodEnabled, true); // this is an important optimization, it tells Qt // that TerminalDisplay will handle repainting its entire area. - setAttribute(Qt::WA_OpaquePaintEvent); +// setAttribute(Qt::WA_OpaquePaintEvent); - _gridLayout = new QGridLayout(this); - _gridLayout->setContentsMargins(0, 0, 0, 0); +// _gridLayout = new QGridLayout(this); +// _gridLayout->setContentsMargins(0, 0, 0, 0); - setLayout( _gridLayout ); +// setLayout( _gridLayout ); - new AutoScrollHandler(this); +// new AutoScrollHandler(this); } TerminalDisplay::~TerminalDisplay() @@ -687,7 +701,7 @@ void TerminalDisplay::drawCharacters(QPainter& painter, // don't draw text which is currently blinking if ( _blinking && (style->rendition & RE_BLINK) ) return; - + // setup bold and underline bool useBold; ColorEntry::FontWeight weight = style->fontWeight(_colorTable); @@ -697,6 +711,8 @@ void TerminalDisplay::drawCharacters(QPainter& painter, useBold = (weight == ColorEntry::Bold) ? true : false; bool useUnderline = style->rendition & RE_UNDERLINE || font().underline(); + painter.setFont(font()); + QFont font = painter.font(); if ( font.bold() != useBold || font.underline() != useUnderline ) @@ -890,7 +906,7 @@ void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion) Q_ASSERT(scrollRect.isValid() && !scrollRect.isEmpty()); //scroll the display vertically to match internal _image - scroll( 0 , _fontHeight * (-lines) , scrollRect ); + //scroll( 0 , _fontHeight * (-lines) , scrollRect ); } QRegion TerminalDisplay::hotSpotRegion() const @@ -951,7 +967,7 @@ void TerminalDisplay::processFilters() update( preUpdateHotSpots | postUpdateHotSpots ); } -void TerminalDisplay::updateImage() +void TerminalDisplay::updateImage() { if ( !_screenWindow ) return; @@ -1015,7 +1031,7 @@ void TerminalDisplay::updateImage() for( x = 0 ; x < columnsToUpdate ; ++x) { - if ( newLine[x] != currentLine[x] ) + if ( newLine[x] != currentLine[x] ) { dirtyMask[x] = true; } @@ -1140,32 +1156,32 @@ void TerminalDisplay::updateImage() void TerminalDisplay::showResizeNotification() { - if (_terminalSizeHint && isVisible()) - { - if (_terminalSizeStartup) { - _terminalSizeStartup=false; - return; - } - if (!_resizeWidget) - { - _resizeWidget = new QLabel("Size: XXX x XXX", this); - _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width("Size: XXX x XXX")); - _resizeWidget->setMinimumHeight(_resizeWidget->sizeHint().height()); - _resizeWidget->setAlignment(Qt::AlignCenter); +// if (_terminalSizeHint && isVisible()) +// { +// if (_terminalSizeStartup) { +// _terminalSizeStartup=false; +// return; +// } +// if (!_resizeWidget) +// { +// _resizeWidget = new QLabel("Size: XXX x XXX", this); +// _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width("Size: XXX x XXX")); +// _resizeWidget->setMinimumHeight(_resizeWidget->sizeHint().height()); +// _resizeWidget->setAlignment(Qt::AlignCenter); - _resizeWidget->setStyleSheet("background-color:palette(window);border-style:solid;border-width:1px;border-color:palette(dark)"); +// _resizeWidget->setStyleSheet("background-color:palette(window);border-style:solid;border-width:1px;border-color:palette(dark)"); - _resizeTimer = new QTimer(this); - _resizeTimer->setSingleShot(true); - connect(_resizeTimer, SIGNAL(timeout()), _resizeWidget, SLOT(hide())); - } - QString sizeStr = QString("Size: %1 x %2").arg(_columns).arg(_lines); - _resizeWidget->setText(sizeStr); - _resizeWidget->move((width()-_resizeWidget->width())/2, - (height()-_resizeWidget->height())/2+20); - _resizeWidget->show(); - _resizeTimer->start(1000); - } +// _resizeTimer = new QTimer(this); +// _resizeTimer->setSingleShot(true); +// connect(_resizeTimer, SIGNAL(timeout()), _resizeWidget, SLOT(hide())); +// } +// QString sizeStr = QString("Size: %1 x %2").arg(_columns).arg(_lines); +// _resizeWidget->setText(sizeStr); +// _resizeWidget->move((width()-_resizeWidget->width())/2, +// (height()-_resizeWidget->height())/2+20); +// _resizeWidget->show(); +// _resizeTimer->start(1000); +// } } void TerminalDisplay::setBlinkingCursor(bool blink) @@ -1227,20 +1243,32 @@ void TerminalDisplay::focusInEvent(QFocusEvent*) _blinkTimer->start(); } -void TerminalDisplay::paintEvent( QPaintEvent* pe ) +void TerminalDisplay::paint(QPainter *painter) { - QPainter paint(this); + //contentsBoundingRect() + // TODO This function might be optimized. + QRect rect = contentsRect(); + drawBackground(*painter, rect, m_palette.background().color(), false /* use opacity setting */); + drawContents(*painter, rect); - foreach (const QRect &rect, (pe->region() & contentsRect()).rects()) - { - drawBackground(paint,rect,palette().background().color(), - true /* use opacity setting */); - drawContents(paint, rect); - } - drawInputMethodPreeditString(paint,preeditRect()); - paintFilters(paint); + //drawInputMethodPreeditString(*painter, preeditRect()); + //paintFilters(painter); } +//void TerminalDisplay::paintEvent( QPaintEvent* pe ) +//{ +// QPainter paint(this); + +// foreach (const QRect &rect, (pe->region() & contentsRect()).rects()) +// { +// drawBackground(paint,rect,palette().background().color(), +// true /* use opacity setting */); +// drawContents(paint, rect); +// } +// drawInputMethodPreeditString(paint,preeditRect()); +// paintFilters(paint); +//} + QPoint TerminalDisplay::cursorPosition() const { if (_screenWindow) @@ -1290,7 +1318,7 @@ void TerminalDisplay::paintFilters(QPainter& painter) { // get color of character under mouse and use it to draw // lines for filters - QPoint cursorPos = mapFromGlobal(QCursor::pos()); + QPoint cursorPos = mapFromScene(QCursor::pos()).toPoint(); int cursorLine; int cursorColumn; int scrollBarWidth = (_scrollbarLocation == ScrollBarLeft) ? _scrollBar->width() : 0; @@ -1383,7 +1411,7 @@ void TerminalDisplay::paintFilters(QPainter& painter) int baseline = r.bottom() - metrics.descent(); // find the position of the underline below that int underlinePos = baseline + metrics.underlinePos(); - if ( region.contains( mapFromGlobal(QCursor::pos()) ) ){ + if ( region.contains( mapFromScene(QCursor::pos()).toPoint() ) ){ painter.drawLine( r.left() , underlinePos , r.right() , underlinePos ); } @@ -1409,6 +1437,9 @@ void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect) int rlx = qMin(_usedColumns-1, qMax(0,(rect.right() - tLx - _leftMargin ) / _fontWidth)); int rly = qMin(_usedLines-1, qMax(0,(rect.bottom() - tLy - _topMargin ) / _fontHeight)); + if(!_image) + return; + const int bufferSize = _usedColumns; QString unistr; unistr.reserve(bufferSize); @@ -1587,9 +1618,9 @@ void TerminalDisplay::propagateSize() if (_isFixedSize) { setSize(_columns, _lines); - QWidget::setFixedSize(sizeHint()); - parentWidget()->adjustSize(); - parentWidget()->setFixedSize(parentWidget()->sizeHint()); + //QWidget::setFixedSize(sizeHint()); + //parentWidget()->adjustSize(); + //parentWidget()->setFixedSize(parentWidget()->sizeHint()); return; } if (_image) @@ -1610,7 +1641,7 @@ void TerminalDisplay::updateImageSize() if (oldimg) { - for (int line = 0; line < lines; line++) + for (int line = 0; line < lines; line++) { memcpy((void*)&_image[_columns*line], (void*)&oldimg[oldcol*line],columns*sizeof(Character)); @@ -1853,7 +1884,7 @@ void TerminalDisplay::mouseMoveEvent(QMouseEvent* ev) const QString& tooltip = spot->tooltip(); if ( !tooltip.isEmpty() ) { - QToolTip::showText( mapToGlobal(ev->pos()) , tooltip , this , _mouseOverHotspotArea.boundingRect() ); + //QToolTip::showText( mapToScene(ev->pos()).toPoint() , tooltip , this , _mouseOverHotspotArea.boundingRect() ); } update( _mouseOverHotspotArea | previousHotspotArea ); @@ -2429,7 +2460,8 @@ bool TerminalDisplay::focusNextPrevChild( bool next ) if (next) return false; // This disables changing the active part in konqueror // when pressing Tab - return QWidget::focusNextPrevChild( next ); + return false; + //return QWidget::focusNextPrevChild( next ); } @@ -2738,7 +2770,7 @@ bool TerminalDisplay::event(QEvent* event) default: break; } - return eventHandled ? true : QWidget::event(event); + return eventHandled ? true : QQuickItem::event(event); } void TerminalDisplay::setBellMode(int mode) @@ -2874,7 +2906,9 @@ void TerminalDisplay::setSize(int columns, int lines) if ( newSize != size() ) { _size = newSize; - updateGeometry(); + //updateGeometry(); + //TODO Manage geometry change + } } @@ -2894,7 +2928,7 @@ void TerminalDisplay::setFixedSize(int cols, int lins) makeImage(); } setSize(cols, lins); - QWidget::setFixedSize(_size); + //QWidget::setFixedSize(_size); } QSize TerminalDisplay::sizeHint() const @@ -2971,39 +3005,39 @@ void TerminalDisplay::doDrag() void TerminalDisplay::outputSuspended(bool suspended) { //create the label when this function is first called - if (!_outputSuspendedLabel) - { - //This label includes a link to an English language website - //describing the 'flow control' (Xon/Xoff) feature found in almost - //all terminal emulators. - //If there isn't a suitable article available in the target language the link - //can simply be removed. - _outputSuspendedLabel = new QLabel( tr("Output has been " - "suspended" - " by pressing Ctrl+S." - " Press Ctrl+Q to resume."), - this ); +// if (!_outputSuspendedLabel) +// { +// //This label includes a link to an English language website +// //describing the 'flow control' (Xon/Xoff) feature found in almost +// //all terminal emulators. +// //If there isn't a suitable article available in the target language the link +// //can simply be removed. +// _outputSuspendedLabel = new QLabel( tr("Output has been " +// "suspended" +// " by pressing Ctrl+S." +// " Press Ctrl+Q to resume."), +// this ); - QPalette palette(_outputSuspendedLabel->palette()); - //KColorScheme::adjustBackground(palette,KColorScheme::NeutralBackground); - _outputSuspendedLabel->setPalette(palette); - _outputSuspendedLabel->setAutoFillBackground(true); - _outputSuspendedLabel->setBackgroundRole(QPalette::Base); - _outputSuspendedLabel->setFont(QApplication::font()); - _outputSuspendedLabel->setContentsMargins(5, 5, 5, 5); +// QPalette palette(_outputSuspendedLabel->palette()); +// //KColorScheme::adjustBackground(palette,KColorScheme::NeutralBackground); +// _outputSuspendedLabel->setPalette(palette); +// _outputSuspendedLabel->setAutoFillBackground(true); +// _outputSuspendedLabel->setBackgroundRole(QPalette::Base); +// _outputSuspendedLabel->setFont(QApplication::font()); +// _outputSuspendedLabel->setContentsMargins(5, 5, 5, 5); - //enable activation of "Xon/Xoff" link in label - _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | - Qt::LinksAccessibleByKeyboard); - _outputSuspendedLabel->setOpenExternalLinks(true); - _outputSuspendedLabel->setVisible(false); +// //enable activation of "Xon/Xoff" link in label +// _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | +// Qt::LinksAccessibleByKeyboard); +// _outputSuspendedLabel->setOpenExternalLinks(true); +// _outputSuspendedLabel->setVisible(false); - _gridLayout->addWidget(_outputSuspendedLabel); - _gridLayout->addItem( new QSpacerItem(0,0,QSizePolicy::Expanding, - QSizePolicy::Expanding), - 1,0); +// _gridLayout->addWidget(_outputSuspendedLabel); +// _gridLayout->addItem( new QSpacerItem(0,0,QSizePolicy::Expanding, +// QSizePolicy::Expanding), +// 1,0); - } +// } _outputSuspendedLabel->setVisible(suspended); } @@ -3036,7 +3070,7 @@ void AutoScrollHandler::timerEvent(QTimerEvent* event) Qt::LeftButton, Qt::NoModifier); - QApplication::sendEvent(widget(),&mouseEvent); + QApplication::sendEvent(widget(),&mouseEvent); } bool AutoScrollHandler::eventFilter(QObject* watched,QEvent* event) { @@ -3077,4 +3111,132 @@ bool AutoScrollHandler::eventFilter(QObject* watched,QEvent* event) return false; } +// QMLTermWidget specific functions /////////////////////////////////////////// + +void TerminalDisplay::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (newGeometry != oldGeometry) { + resizeEvent(NULL); + update(); + } + + QQuickPaintedItem::geometryChanged(newGeometry,oldGeometry); +} + +void TerminalDisplay::update(const QRegion ®ion) +{ + Q_UNUSED(region); + // TODO this function might be optimized +// foreach (QRect rect, region.rects()) { +// QQuickPaintedItem::update(rect); +// } + QQuickPaintedItem::update(); +} + +void TerminalDisplay::update() +{ + QQuickPaintedItem::update(); +} + +QRect TerminalDisplay::contentsRect() const +{ + return QRect(0, 0, this->width(), this->height()); +} + +QSize TerminalDisplay::size() const +{ + return QSize(this->width(), this->height()); +} + +void TerminalDisplay::setSession(KSession * session) +{ + if (m_session != session) { + qDebug() << "SetSession called"; + session->removeView(this); + m_session = session; + + connect(this, SIGNAL(copyAvailable(bool)), + m_session, SLOT(selectionChanged(bool))); + connect(this, SIGNAL(termGetFocus()), + m_session, SIGNAL(termGetFocus())); + connect(this, SIGNAL(termLostFocus()), + m_session, SIGNAL(termLostFocus())); + connect(this, SIGNAL(keyPressedSignal(QKeyEvent *)), + m_session, SIGNAL(termKeyPressed(QKeyEvent *))); + + m_session->addView(this); + + setRandomSeed(m_session->getRandomSeed()); + update(); + emit sessionChanged(); + } +} + + +KSession* TerminalDisplay::getSession() +{ + return m_session; +} + +QStringList TerminalDisplay::availableColorSchemes() +{ + QStringList ret; + foreach (const ColorScheme* cs, ColorSchemeManager::instance()->allColorSchemes()) + ret.append(cs->name()); + return ret; +} + +void TerminalDisplay::setColorScheme(const QString &name) +{ + const ColorScheme *cs; + // avoid legacy (int) solution + if (!availableColorSchemes().contains(name)) + cs = ColorSchemeManager::instance()->defaultColorScheme(); + else + cs = ColorSchemeManager::instance()->findColorScheme(name); + + if (! cs) + { + qDebug() << "Cannot load color scheme: " << name; + return; + } + + ColorEntry table[TABLE_COLORS]; + cs->getColorTable(table); + setColorTable(table); +} + +void TerminalDisplay::simulateKeyPress(int key, int modifiers, bool pressed, quint32 nativeScanCode, const QString &text) +{ + Q_UNUSED(nativeScanCode); + QEvent::Type type = pressed ? QEvent::KeyPress : QEvent::KeyRelease; + QKeyEvent event = QKeyEvent(type, key, (Qt::KeyboardModifier) modifiers, text); + keyPressedSignal(&event); +} + +void TerminalDisplay::simulateWheel(int x, int y, int buttons, int modifiers, QPointF angleDelta){ + QWheelEvent event(QPointF(x,y), angleDelta.y(), (Qt::MouseButton) buttons, (Qt::KeyboardModifier) modifiers); + wheelEvent(&event); +} + +void TerminalDisplay::simulateMouseMove(int x, int y, int button, int buttons, int modifiers){ + QMouseEvent event(QEvent::MouseMove, QPointF(x, y),(Qt::MouseButton) button, (Qt::MouseButtons) buttons, (Qt::KeyboardModifiers) modifiers); + mouseMoveEvent(&event); +} + +void TerminalDisplay::simulateMousePress(int x, int y, int button, int buttons, int modifiers){ + QMouseEvent event(QEvent::MouseButtonPress, QPointF(x, y),(Qt::MouseButton) button, (Qt::MouseButtons) buttons, (Qt::KeyboardModifiers) modifiers); + mousePressEvent(&event); +} + +void TerminalDisplay::simulateMouseRelease(int x, int y, int button, int buttons, int modifiers){ + QMouseEvent event(QEvent::MouseButtonRelease, QPointF(x, y),(Qt::MouseButton) button, (Qt::MouseButtons) buttons, (Qt::KeyboardModifiers) modifiers); + mouseReleaseEvent(&event); +} + +void TerminalDisplay::simulateMouseDoubleClick(int x, int y, int button, int buttons, int modifiers){ + QMouseEvent event(QEvent::MouseButtonDblClick, QPointF(x, y),(Qt::MouseButton) button, (Qt::MouseButtons) buttons, (Qt::KeyboardModifiers) modifiers); + mouseDoubleClickEvent(&event); +} + //#include "TerminalDisplay.moc" diff --git a/lib/TerminalDisplay.h b/lib/TerminalDisplay.h index c32d799..ab88e90 100644 --- a/lib/TerminalDisplay.h +++ b/lib/TerminalDisplay.h @@ -24,7 +24,8 @@ // Qt #include #include -#include +//#include +#include // Konsole #include "Filter.h" @@ -32,6 +33,10 @@ //#include "konsole_export.h" #define KONSOLEPRIVATE_EXPORT +// QMLTermWidget +#include "ksession.h" +#include "ColorScheme.h" + class QDrag; class QDragEnterEvent; class QDropEvent; @@ -75,13 +80,17 @@ class ScreenWindow; * * TODO More documentation */ -class KONSOLEPRIVATE_EXPORT TerminalDisplay : public QWidget +class KONSOLEPRIVATE_EXPORT TerminalDisplay : public QQuickPaintedItem { Q_OBJECT + Q_PROPERTY(KSession* session READ getSession WRITE setSession NOTIFY sessionChanged) + Q_PROPERTY(QFont font READ getVTFont WRITE setVTFont ) + Q_PROPERTY(QString colorScheme WRITE setColorScheme ) + public: /** Constructs a new terminal display widget with the specified parent. */ - TerminalDisplay(QWidget *parent=0); + TerminalDisplay(QQuickItem *parent=0); virtual ~TerminalDisplay(); /** Returns the terminal color palette used by the display. */ @@ -431,7 +440,7 @@ public: // maps a point on the widget to the position ( ie. line and column ) // of the character at that point. - void getCharacterPosition(const QPoint& widgetPoint,int& line,int& column) const; + void getCharacterPosition(const QPoint &widgetPoint, int& line, int& column) const; public slots: @@ -525,6 +534,17 @@ public slots: void selectionChanged(); + // QMLTermWidget + void setColorScheme(const QString &name); + QStringList availableColorSchemes(); + + void simulateKeyPress(int key, int modifiers, bool pressed, quint32 nativeScanCode, const QString &text); + void simulateWheel(int x, int y, int buttons, int modifiers, QPointF angleDelta); + void simulateMouseMove(int x, int y, int button, int buttons, int modifiers); + void simulateMousePress(int x, int y, int button, int buttons, int modifiers); + void simulateMouseRelease(int x, int y, int button, int buttons, int modifiers); + void simulateMouseDoubleClick(int x, int y, int button, int buttons, int modifiers); + signals: /** @@ -572,10 +592,13 @@ signals: void notifyBell(const QString&); + // QMLTermWidget + void sessionChanged(); + protected: virtual bool event( QEvent * ); - virtual void paintEvent( QPaintEvent * ); + //virtual void paintEvent( QPaintEvent * ); virtual void showEvent(QShowEvent*); virtual void hideEvent(QHideEvent*); @@ -622,6 +645,10 @@ protected: virtual void inputMethodEvent ( QInputMethodEvent* event ); virtual QVariant inputMethodQuery( Qt::InputMethodQuery query ) const; + // QMLTermWidget + void paint(QPainter * painter); + virtual void geometryChanged(const QRectF & newGeometry, const QRectF & oldGeometry); + protected slots: void scrollBarPositionChanged(int value); @@ -831,6 +858,29 @@ private: static const int DEFAULT_LEFT_MARGIN = 1; static const int DEFAULT_TOP_MARGIN = 1; + // QMLTermWidget port functions + QFont m_font; + QPalette m_palette; + QPalette::ColorRole m_color_role; + KSession *m_session; + + QFont font() const { return m_font; } + + const QPalette palette() { return m_palette; } + void setPalette(const QPalette &p){ m_palette = p; } + + QPalette::ColorRole backgroundRole() { return m_color_role; } + void setBackgroundRole(QPalette::ColorRole role) { m_color_role = role; } + + void update(const QRegion ®ion); + void update(); + + QRect contentsRect() const; + QSize size() const; + + void setSession(KSession *session); + KSession* getSession(); + public: static void setTransparencyEnabled(bool enable) { diff --git a/lib/tools.cpp b/lib/tools.cpp index 487495d..fef81c1 100644 --- a/lib/tools.cpp +++ b/lib/tools.cpp @@ -17,7 +17,7 @@ QString get_kb_layout_dir() // qDebug() << __FILE__ << __FUNCTION__; QString rval = ""; - QString k(KB_LAYOUT_DIR); + QString k(qgetenv("KB_LAYOUT_DIR")); QDir d(k); qDebug() << "default KB_LAYOUT_DIR: " << k; @@ -55,7 +55,7 @@ QString get_color_schemes_dir() // qDebug() << __FILE__ << __FUNCTION__; QString rval = ""; - QString k(COLORSCHEMES_DIR); + QString k(qgetenv("COLORSCHEMES_DIR")); QDir d(k); // qDebug() << "default COLORSCHEMES_DIR: " << k; diff --git a/qmltermwidget.pro b/qmltermwidget.pro new file mode 100644 index 0000000..01946cc --- /dev/null +++ b/qmltermwidget.pro @@ -0,0 +1,26 @@ +TEMPLATE = lib +TARGET = qmltermwidget +QT += qml quick widgets +CONFIG += qt plugin + +include(lib.pri) + +DESTDIR = $$OUT_PWD/QMLTermWidget + +DEFINES += HAVE_POSIX_OPENPT HAVE_SYS_TIME_H +!macx:DEFINES += HAVE_UPDWTMPX + +INCLUDEPATH += $$PWD/lib +DEPENDPATH += $$PWD/lib +INCLUDEPATH += $$PWD/src + +HEADERS += $$PWD/src/qmltermwidget_plugin.h \ + $$PWD/src/ksession.h + +SOURCES += $$PWD/src/qmltermwidget_plugin.cpp \ + $$PWD/src/ksession.cpp + +# Copy the files useful to the plugin in DESTDIR +QMAKE_POST_LINK = $(COPY_DIR) $$PWD/lib/color-schemes $$DESTDIR && \ + $(COPY_DIR) $$PWD/lib/kb-layouts $$DESTDIR && \ + $$QMAKE_COPY $$PWD/src/qmldir $$DESTDIR diff --git a/src/ksession.cpp b/src/ksession.cpp new file mode 100644 index 0000000..f3ab2c6 --- /dev/null +++ b/src/ksession.cpp @@ -0,0 +1,248 @@ +/* + This file is part of Konsole QML plugin, + which is a terminal emulator from KDE. + + Copyright 2013 by Dmitry Zagnoyko + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "ksession.h" + +// Qt +#include + +// Konsole +#include "KeyboardTranslator.h" + +KSession::KSession(QObject *parent) : + QObject(parent), m_session(createSession("")) +{ + connect(m_session, SIGNAL(finished()), this, SLOT(sessionFinished())); + connect(m_session, SIGNAL(titleChanged()), this, SIGNAL(titleChanged())); +} + +KSession::~KSession() +{ + if (m_session) { + m_session->close(); + m_session->disconnect(); + delete m_session; + } +} + +void KSession::setTitle(QString name) +{ + m_session->setTitle(Session::NameRole, name); +} + + +Session *KSession::createSession(QString name) +{ + Session *session = new Session(); + + session->setTitle(Session::NameRole, name); + + /* Thats a freaking bad idea!!!! + * /bin/bash is not there on every system + * better set it to the current $SHELL + * Maybe you can also make a list available and then let the widget-owner decide what to use. + * By setting it to $SHELL right away we actually make the first filecheck obsolete. + * But as iam not sure if you want to do anything else ill just let both checks in and set this to $SHELL anyway. + */ + + //cool-old-term: There is another check in the code. Not sure if useful. + + QString envshell = getenv("SHELL"); + QString shellProg = envshell != NULL ? envshell : "/bin/bash"; + session->setProgram(shellProg); + + setenv("TERM", "xterm", 1); + + //session->setProgram(); + + QStringList args(""); + session->setArguments(args); + session->setAutoClose(true); + + session->setCodec(QTextCodec::codecForName("UTF-8")); + + session->setFlowControlEnabled(true); + session->setHistoryType(HistoryTypeBuffer(1000)); + + session->setDarkBackground(true); + + session->setKeyBindings(""); + + return session; +} + +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + + +int KSession::getRandomSeed() +{ + return m_session->sessionId() * 31; +} + +void KSession::addView(TerminalDisplay *display) +{ + m_session->addView(display); +} + +void KSession::removeView(TerminalDisplay *display) +{ + m_session->removeView(display); +} + +void KSession::sessionFinished() +{ + emit finished(); +} + +void KSession::selectionChanged(bool textSelected) +{ + Q_UNUSED(textSelected) +} + +void KSession::startShellProgram() +{ + if ( m_session->isRunning() ) { + return; + } + + m_session->run(); +} + +int KSession::getShellPID() +{ + return m_session->processId(); +} + +void KSession::changeDir(const QString &dir) +{ + /* + this is a very hackish way of trying to determine if the shell is in + the foreground before attempting to change the directory. It may not + be portable to anything other than Linux. + */ + QString strCmd; + strCmd.setNum(getShellPID()); + strCmd.prepend("ps -j "); + strCmd.append(" | tail -1 | awk '{ print $5 }' | grep -q \\+"); + int retval = system(strCmd.toStdString().c_str()); + + if (!retval) { + QString cmd = "cd " + dir + "\n"; + sendText(cmd); + } +} + +void KSession::setEnvironment(const QStringList &environment) +{ + m_session->setEnvironment(environment); +} + + +void KSession::setShellProgram(const QString &progname) +{ + m_session->setProgram(progname); +} + +void KSession::setInitialWorkingDirectory(const QString &dir) +{ + _initialWorkingDirectory = dir; + m_session->setInitialWorkingDirectory(dir); +} + +QString KSession::getInitialWorkingDirectory() +{ + return _initialWorkingDirectory; +} + +void KSession::setArgs(QStringList &args) +{ + m_session->setArguments(args); +} + +void KSession::setTextCodec(QTextCodec *codec) +{ + m_session->setCodec(codec); +} + +void KSession::setHistorySize(int lines) +{ + if (lines < 0) + m_session->setHistoryType(HistoryTypeFile()); + else + m_session->setHistoryType(HistoryTypeBuffer(lines)); +} + +void KSession::sendText(QString text) +{ + m_session->sendText(text); +} + +void KSession::sendKey(int rep, int key, int mod) const +{ + //TODO implement or remove this function. +// Qt::KeyboardModifier kbm = Qt::KeyboardModifier(mod); + +// QKeyEvent qkey(QEvent::KeyPress, key, kbm); + +// while (rep > 0){ +// m_session->sendKey(&qkey); +// --rep; +// } +} + +void KSession::setFlowControlEnabled(bool enabled) +{ + m_session->setFlowControlEnabled(enabled); +} + +bool KSession::flowControlEnabled() +{ + return m_session->flowControlEnabled(); +} + +void KSession::setKeyBindings(const QString &kb) +{ + m_session->setKeyBindings(kb); + emit changedKeyBindings(kb); +} + +QString KSession::getKeyBindings() +{ + return m_session->keyBindings(); +} + +QStringList KSession::availableKeyBindings() +{ + return KeyboardTranslatorManager::instance()->allTranslators(); +} + +QString KSession::keyBindings() +{ + return m_session->keyBindings(); +} + +QString KSession::getTitle() +{ + return m_session->userTitle(); +} diff --git a/src/ksession.h b/src/ksession.h new file mode 100644 index 0000000..6d20ef9 --- /dev/null +++ b/src/ksession.h @@ -0,0 +1,142 @@ +/* + This file is part of Konsole QML plugin, + which is a terminal emulator from KDE. + + Copyright 2013 by Dmitry Zagnoyko + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +#ifndef KSESSION_H +#define KSESSION_H + +#include + +// Konsole +#include "Session.h" +//#include "TerminalDisplay.h" + +using namespace Konsole; + +class KSession : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString kbScheme READ getKeyBindings WRITE setKeyBindings NOTIFY changedKeyBindings) + Q_PROPERTY(QString initialWorkingDirectory READ getInitialWorkingDirectory WRITE setInitialWorkingDirectory) + Q_PROPERTY(QString title READ getTitle NOTIFY titleChanged) + Q_PROPERTY(QString shellProgram WRITE setShellProgram) + +public: + KSession(QObject *parent = 0); + ~KSession(); + +public: + //bool setup(); + void addView(TerminalDisplay *display); + void removeView(TerminalDisplay *display); + + int getRandomSeed(); + QString getKeyBindings(); + + //look-n-feel, if you don`t like defaults + + //environment + void setEnvironment(const QStringList & environment); + + //Initial working directory + void setInitialWorkingDirectory(const QString & dir); + QString getInitialWorkingDirectory(); + + // Shell program args, default is none + void setArgs(QStringList & args); + + //Text codec, default is UTF-8 + void setTextCodec(QTextCodec * codec); + + // History size for scrolling + void setHistorySize(int lines); //infinite if lines < 0 + + // Sets whether flow control is enabled + void setFlowControlEnabled(bool enabled); + + // Returns whether flow control is enabled + bool flowControlEnabled(void); + + /** + * Sets whether the flow control warning box should be shown + * when the flow control stop key (Ctrl+S) is pressed. + */ + //void setFlowControlWarningEnabled(bool enabled); + + /*! Get all available keyboard bindings + */ + static QStringList availableKeyBindings(); + + //! Return current key bindings + QString keyBindings(); + + QString getTitle(); + +signals: + void finished(); + void copyAvailable(bool); + + void termGetFocus(); + void termLostFocus(); + + void termKeyPressed(QKeyEvent *); + + void changedKeyBindings(QString kb); + + void titleChanged(); + + +public slots: + /*! Set named key binding for given widget + */ + void setKeyBindings(const QString & kb); + void setTitle(QString name); + + void startShellProgram(); + + // Shell program, default is /bin/bash + void setShellProgram(const QString & progname); + + int getShellPID(); + void changeDir(const QString & dir); + + // Send some text to terminal + void sendText(QString text); + // Send some text to terminal + void sendKey(int rep, int key, int mod) const; + + +protected slots: + void sessionFinished(); + void selectionChanged(bool textSelected); + +private slots: + Session* createSession(QString name); + //Konsole::KTerminalDisplay* createTerminalDisplay(Konsole::Session *session, QQuickItem* parent); + +private: + //Konsole::KTerminalDisplay *m_terminalDisplay; + QString _initialWorkingDirectory; + Session *m_session; + +}; + +#endif // KSESSION_H diff --git a/src/qmldir b/src/qmldir new file mode 100644 index 0000000..284b80e --- /dev/null +++ b/src/qmldir @@ -0,0 +1,3 @@ +module QMLTermWidget +plugin qmltermwidget + diff --git a/src/qmltermwidget_plugin.cpp b/src/qmltermwidget_plugin.cpp new file mode 100644 index 0000000..656b115 --- /dev/null +++ b/src/qmltermwidget_plugin.cpp @@ -0,0 +1,38 @@ +#include "qmltermwidget_plugin.h" + +#include "TerminalDisplay.h" +#include "ksession.h" + +#include +#include +#include + +using namespace Konsole; + +void QmltermwidgetPlugin::registerTypes(const char *uri) +{ + // @uri org.qterminal.qmlterminal + qmlRegisterType(uri, 1, 0, "QMLTermWidget"); + qmlRegisterType(uri, 1, 0, "QMLTermSession"); +} + +void QmltermwidgetPlugin::initializeEngine(QQmlEngine *engine, const char *uri) +{ + QQmlExtensionPlugin::initializeEngine(engine, uri); + + QStringList pwds = engine->importPathList(); + + if (!pwds.empty()){ + + QString cs, kbl; + + foreach (QString pwd, pwds) { + cs = pwd + "/QMLTermWidget/color-schemes"; + kbl = pwd + "/QMLTermWidget/kb-layouts"; + if (QDir(cs).exists()) break; + } + + setenv("KB_LAYOUT_DIR",kbl.toUtf8().constData(),1); + setenv("COLORSCHEMES_DIR",cs.toUtf8().constData(),1); + } +} diff --git a/src/qmltermwidget_plugin.h b/src/qmltermwidget_plugin.h new file mode 100644 index 0000000..1a9531b --- /dev/null +++ b/src/qmltermwidget_plugin.h @@ -0,0 +1,17 @@ +#ifndef QMLTERMWIDGET_PLUGIN_H +#define QMLTERMWIDGET_PLUGIN_H + +#include + +class QmltermwidgetPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qterminal.qmlterminal.QMLTermWidget") + +public: + void registerTypes(const char *uri); + void initializeEngine(QQmlEngine *engine, const char *uri); +}; + +#endif // QMLTERMWIDGET_PLUGIN_H + diff --git a/test-app/test-app.qml b/test-app/test-app.qml new file mode 100644 index 0000000..e19f758 --- /dev/null +++ b/test-app/test-app.qml @@ -0,0 +1,32 @@ +import QtQuick 2.0 +import QMLTermWidget 1.0 +import QtQuick.Controls 1.2 + +Rectangle { + width: 640 + height: 480 + + Action{ + onTriggered: terminal.copyClipboard(); + shortcut: "Ctrl+Shift+C" + } + + Action{ + onTriggered: terminal.pasteClipboard(); + shortcut: "Ctrl+Shift+V" + } + + QMLTermWidget { + id: terminal + anchors.fill: parent + font.family: "Monospace" + font.pointSize: 12 + colorScheme: "DarkPastels" + session: QMLTermSession{ + id: mainsession + initialWorkingDirectory: "$HOME" + } + Component.onCompleted: mainsession.startShellProgram(); + } + Component.onCompleted: terminal.forceActiveFocus(); +}