diff --git a/config-kwin.h.cmake b/config-kwin.h.cmake --- a/config-kwin.h.cmake +++ b/config-kwin.h.cmake @@ -3,6 +3,7 @@ #cmakedefine KWIN_BUILD_ACTIVITIES 1 #define KWIN_NAME "${KWIN_NAME}" #define KWIN_INTERNAL_NAME_X11 "${KWIN_INTERNAL_NAME_X11}" +#define KWIN_WAYLAND_BINARY_PATH "${CMAKE_INSTALL_FULL_BINDIR}/kwin_wayland" #define KWIN_CONFIG "${KWIN_NAME}rc" #define KWIN_VERSION_STRING "${PROJECT_VERSION}" #define XCB_VERSION_STRING "${XCB_VERSION}" diff --git a/main_wayland.cpp b/main_wayland.cpp --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -379,7 +379,7 @@ static QString automaticBackendSelection() { - if (qEnvironmentVariableIsSet("WAYLAND_DISPLAY")) { + if (qEnvironmentVariableIsSet("WAYLAND_DISPLAY") || qEnvironmentVariableIsSet("WAYLAND_SOCKET") ) { return s_waylandPlugin; } if (qEnvironmentVariableIsSet("DISPLAY")) { diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(kglobalaccel) +add_subdirectory(qml) add_subdirectory(qpa) add_subdirectory(idletime) add_subdirectory(platforms) diff --git a/plugins/qml/CMakeLists.txt b/plugins/qml/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/plugins/qml/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(kwinqml) diff --git a/plugins/qml/kwinqml/CMakeLists.txt b/plugins/qml/kwinqml/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/plugins/qml/kwinqml/CMakeLists.txt @@ -0,0 +1,17 @@ +set(kwinqmlplugin_SRCS + kwinqml.cpp + kwinplugin.cpp +) + +install(FILES qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/kwin/qml) + +add_library(kwinqmlplugin SHARED ${kwinqmlplugin_SRCS}) + +target_link_libraries(kwinqmlplugin + Qt5::Qml + Qt5::Quick + KF5::WaylandServer + KF5::WindowSystem +) + +install(TARGETS kwinqmlplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/kwin/qml) diff --git a/plugins/qml/kwinqml/kwinplugin.h b/plugins/qml/kwinqml/kwinplugin.h new file mode 100644 --- /dev/null +++ b/plugins/qml/kwinqml/kwinplugin.h @@ -0,0 +1,33 @@ +/******************************************************************** +Copyright (C) 2016 Bhavisha Dhruve + +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, see . +*********************************************************************/ + +#ifndef KWINPLUGIN_H +#define KWINPLUGIN_H + +#include +#include + +class KWinPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") + + public: + void registerTypes(const char *uri) override; +}; + +#endif //KWINPLUGIN_H diff --git a/plugins/qml/kwinqml/kwinplugin.cpp b/plugins/qml/kwinqml/kwinplugin.cpp new file mode 100644 --- /dev/null +++ b/plugins/qml/kwinqml/kwinplugin.cpp @@ -0,0 +1,27 @@ +/******************************************************************** +Copyright (C) 2016 Bhavisha Dhruve + +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, see . +*********************************************************************/ + +#include "kwinplugin.h" +#include "kwinqml.h" +#include + +void KWinPlugin::registerTypes(const char *uri) +{ + Q_ASSERT(uri == QLatin1String("org.kde.kwin.qml")); + qmlRegisterType(uri, 1, 0,"KWinQml"); +} + diff --git a/plugins/qml/kwinqml/kwinqml.h b/plugins/qml/kwinqml/kwinqml.h new file mode 100644 --- /dev/null +++ b/plugins/qml/kwinqml/kwinqml.h @@ -0,0 +1,90 @@ +/******************************************************************** +Copyright (C) 2016 Bhavisha Dhruve + +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, see . +*********************************************************************/ + +#ifndef KWINQML_H +#define KWINQML_H + +#include +#include +#include +#include +#include + +class QElapsedTimer; + +namespace KWayland +{ +namespace Server +{ +class Display; +class SeatInterface; +class ShellInterface; +class SurfaceInterface; +} +} + +class KWinQml : public QQuickItem +{ + Q_OBJECT + /** + * socketName to pass to Wayland Server Display. + **/ + Q_PROPERTY(QString socketName READ socketName WRITE setSocketName NOTIFY socketNameChanged) + +public: + KWinQml(QQuickItem *parent = nullptr); + ~KWinQml() override; + + void setSocketName(const QString &socketName) + { + if (m_socketName != socketName) { + m_socketName = socketName; + emit socketNameChanged(socketName); + } + } + + QString socketName() const + { + return m_socketName; + } + + Q_INVOKABLE void start(); + Q_INVOKABLE void stop(); + QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); + void hoverMoveEvent(QHoverEvent *event); + +signals: + void socketNameChanged(QString); + +private: + QString m_socketName; + KWayland::Server::Display *m_display = nullptr; + KWayland::Server::SeatInterface *m_seat = nullptr; + KWayland::Server::ShellInterface *m_shell = nullptr; + KWayland::Server::SurfaceInterface *m_surface = nullptr; + QProcess *m_kwinWaylandProcess; + QScopedPointer m_timeSinceStart; + QSGTexture *m_texture = nullptr; + +}; + +#endif //KWINQML_H + diff --git a/plugins/qml/kwinqml/kwinqml.cpp b/plugins/qml/kwinqml/kwinqml.cpp new file mode 100644 --- /dev/null +++ b/plugins/qml/kwinqml/kwinqml.cpp @@ -0,0 +1,177 @@ +/******************************************************************** +Copyright (C) 2016 Bhavisha Dhruve + +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, see . +*********************************************************************/ + +#include "config-kwin.h" +#include "kwinqml.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// system +#include +#include +#include +#include + +using namespace KWayland::Server; + +KWinQml::KWinQml(QQuickItem *parent) + : QQuickItem(parent) + , m_timeSinceStart(new QElapsedTimer) +{ + setFlag(QQuickItem::ItemHasContents, true); + this->setAcceptedMouseButtons(Qt::AllButtons); + setFlag(QQuickItem::ItemAcceptsInputMethod, true); + setFlag(QQuickItem::ItemIsFocusScope, true); + this->setAcceptHoverEvents(true); + +} + +KWinQml::~KWinQml() +{ + stop(); +} + +void KWinQml::start() +{ + Q_ASSERT(!m_display); + m_display = new Display(this); + m_display->start(Display::StartMode::ConnectClientsOnly); + m_display->createShm(); + m_display->createCompositor()->create(); + m_seat = m_display->createSeat(m_display); + m_seat->setHasPointer(true); + m_seat->setHasKeyboard(true); + m_seat->setHasTouch(true); + m_seat->create(); + m_shell = m_display->createShell(m_display); + m_shell->create(); + connect(m_shell, &ShellInterface::surfaceCreated, this, + [this] (ShellSurfaceInterface *shellSurface) { + m_surface = shellSurface->surface(); + m_seat->setFocusedKeyboardSurface(m_surface); + m_seat->setFocusedPointerSurface(m_surface); + connect(m_surface, &SurfaceInterface::damaged, this, + [this] () { + update(); + m_surface->frameRendered(m_timeSinceStart->elapsed()); + } + ); + } + ); + // output + auto output = m_display->createOutput(m_display); + const QSize size(height(), width()); + output->setGlobalPosition(QPoint(0, 0)); + output->setPhysicalSize(size / 3.8); + output->addMode(size); + output->create(); + + int sx[2]; + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) { + return; + } + m_display->createClient(sx[0]); + int socket = dup(sx[1]); + if (socket == -1) { + return; + } + m_kwinWaylandProcess = new QProcess(this); + m_kwinWaylandProcess->setProcessChannelMode(QProcess::ForwardedChannels); + QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); + environment.insert(QStringLiteral("WAYLAND_SOCKET"), QString::fromUtf8(QByteArray::number(socket))); + environment.insert(QStringLiteral("KWIN_COMPOSE"), QStringLiteral("Q")); + m_kwinWaylandProcess->setProcessEnvironment(environment); + m_kwinWaylandProcess->setProcessChannelMode(QProcess::ForwardedChannels); + QStringList arguments{QStringLiteral("--xwayland"), + QStringLiteral("--socket"), m_socketName, + QStringLiteral("--width"), QString::number(width()), + QStringLiteral("--height"), QString::number(height())}; + m_kwinWaylandProcess->start(QStringLiteral(KWIN_WAYLAND_BINARY_PATH), arguments); +} + +QSGNode *KWinQml::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) +{ + if (!m_surface) { + return node; + } + if (m_surface->buffer() == nullptr) { + return node; + } + QSGSimpleTextureNode *n = static_cast(node); + if (!n) { + n = new QSGSimpleTextureNode(); + } + delete(m_texture); + m_texture = nullptr; + m_texture = window()->createTextureFromImage(m_surface->buffer()->data()); + n->setTexture(m_texture); + n->setRect(boundingRect()); + return n; +} + +void KWinQml::mousePressEvent(QMouseEvent *event) +{ + if (m_seat != nullptr) { + m_seat->pointerButtonPressed(event->button()); + } +} + +void KWinQml::mouseReleaseEvent(QMouseEvent *event) +{ + if (m_seat != nullptr) { + m_seat->pointerButtonReleased(event->button()); + } +} + +void KWinQml::keyPressEvent(QKeyEvent *event) +{ + if (m_seat != nullptr) { + const int magicOffset = KWindowSystem:: isPlatformX11() ? 8 : 0; + m_seat->keyPressed(event->nativeScanCode() - magicOffset); + } +} + +void KWinQml::keyReleaseEvent(QKeyEvent *event) +{ + if (m_seat != nullptr) { + const int magicOffset = KWindowSystem:: isPlatformX11() ? 8 : 0; + m_seat->keyReleased(event->nativeScanCode() - magicOffset); + } +} + +void KWinQml::hoverMoveEvent(QHoverEvent *event) +{ + if (m_seat != nullptr) { + m_seat->setPointerPos(event->pos()); + } +} + +void KWinQml::stop() +{ + m_kwinWaylandProcess->terminate(); +} diff --git a/plugins/qml/kwinqml/qmldir b/plugins/qml/kwinqml/qmldir new file mode 100644 --- /dev/null +++ b/plugins/qml/kwinqml/qmldir @@ -0,0 +1,2 @@ +module org.kde.kwin.qml +plugin kwinqmlplugin