File indexing completed on 2024-12-01 05:02:03

0001 /*
0002     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "core/graphicsbufferview.h"
0008 #include "wayland/compositor.h"
0009 #include "wayland/datadevicemanager.h"
0010 #include "wayland/display.h"
0011 #include "wayland/keyboard.h"
0012 #include "wayland/output.h"
0013 #include "wayland/pointer.h"
0014 #include "wayland/seat.h"
0015 #include "wayland/xdgshell.h"
0016 
0017 #include "fakeoutput.h"
0018 
0019 #include <QApplication>
0020 #include <QCommandLineParser>
0021 #include <QDateTime>
0022 #include <QFile>
0023 #include <QKeyEvent>
0024 #include <QMouseEvent>
0025 #include <QPainter>
0026 #include <QPointer>
0027 #include <QThreadPool>
0028 #include <QWidget>
0029 
0030 #include <iostream>
0031 #include <unistd.h>
0032 
0033 static int startXServer()
0034 {
0035     const QByteArray process = QByteArrayLiteral("Xwayland");
0036     int pipeFds[2];
0037     if (pipe(pipeFds) != 0) {
0038         std::cerr << "FATAL ERROR failed to create pipe to start X Server " << process.constData() << std::endl;
0039         exit(1);
0040     }
0041 
0042     pid_t pid = fork();
0043     if (pid == 0) {
0044         // child process - should be turned into Xwayland
0045         // writes to pipe, closes read side
0046         close(pipeFds[0]);
0047         char fdbuf[16];
0048         sprintf(fdbuf, "%d", pipeFds[1]);
0049         execlp(process.constData(), process.constData(), "-displayfd", fdbuf, "-rootless", (char *)nullptr);
0050         close(pipeFds[1]);
0051         exit(20);
0052     }
0053     // parent process - this is the wayland server
0054     // reads from pipe, closes write side
0055     close(pipeFds[1]);
0056     return pipeFds[0];
0057 }
0058 
0059 static void readDisplayFromPipe(int pipe)
0060 {
0061     QFile readPipe;
0062     if (!readPipe.open(pipe, QIODevice::ReadOnly)) {
0063         std::cerr << "FATAL ERROR failed to open pipe to start X Server XWayland" << std::endl;
0064         exit(1);
0065     }
0066     QByteArray displayNumber = readPipe.readLine();
0067 
0068     displayNumber.prepend(QByteArray(":"));
0069     displayNumber.remove(displayNumber.size() - 1, 1);
0070     std::cout << "X-Server started on display " << displayNumber.constData() << std::endl;
0071 
0072     setenv("DISPLAY", displayNumber.constData(), true);
0073 
0074     // close our pipe
0075     close(pipe);
0076 }
0077 
0078 class CompositorWindow : public QWidget
0079 {
0080     Q_OBJECT
0081 public:
0082     explicit CompositorWindow(QWidget *parent = nullptr);
0083     virtual ~CompositorWindow();
0084 
0085     void surfaceCreated(KWin::XdgToplevelInterface *surface);
0086 
0087     void setSeat(const QPointer<KWin::SeatInterface> &seat);
0088 
0089 protected:
0090     void paintEvent(QPaintEvent *event) override;
0091     void keyPressEvent(QKeyEvent *event) override;
0092     void keyReleaseEvent(QKeyEvent *event) override;
0093     void mouseMoveEvent(QMouseEvent *event) override;
0094     void mousePressEvent(QMouseEvent *event) override;
0095     void mouseReleaseEvent(QMouseEvent *event) override;
0096     void wheelEvent(QWheelEvent *event) override;
0097 
0098 private:
0099     void updateFocus();
0100     QList<KWin::XdgToplevelInterface *> m_stackingOrder;
0101     QPointer<KWin::SeatInterface> m_seat;
0102 };
0103 
0104 CompositorWindow::CompositorWindow(QWidget *parent)
0105     : QWidget(parent)
0106 {
0107     setMouseTracking(true);
0108 }
0109 
0110 CompositorWindow::~CompositorWindow() = default;
0111 
0112 void CompositorWindow::surfaceCreated(KWin::XdgToplevelInterface *surface)
0113 {
0114     using namespace KWin;
0115     surface->sendConfigure(QSize(), XdgToplevelInterface::States());
0116     m_stackingOrder << surface;
0117     connect(surface->surface(), &SurfaceInterface::damaged, this, static_cast<void (CompositorWindow::*)()>(&CompositorWindow::update));
0118     connect(surface, &XdgToplevelInterface::destroyed, this, [surface, this] {
0119         m_stackingOrder.removeAll(surface);
0120         updateFocus();
0121         update();
0122     });
0123     updateFocus();
0124 }
0125 
0126 void CompositorWindow::updateFocus()
0127 {
0128     using namespace KWin;
0129     if (!m_seat || m_stackingOrder.isEmpty()) {
0130         return;
0131     }
0132     auto it = std::find_if(m_stackingOrder.constBegin(), m_stackingOrder.constEnd(), [](XdgToplevelInterface *toplevel) {
0133         return toplevel->surface()->isMapped();
0134     });
0135     if (it == m_stackingOrder.constEnd()) {
0136         return;
0137     }
0138     m_seat->notifyPointerEnter((*it)->surface(), m_seat->pointerPos());
0139     m_seat->setFocusedKeyboardSurface((*it)->surface());
0140 }
0141 
0142 void CompositorWindow::setSeat(const QPointer<KWin::SeatInterface> &seat)
0143 {
0144     m_seat = seat;
0145 }
0146 
0147 void CompositorWindow::paintEvent(QPaintEvent *event)
0148 {
0149     QWidget::paintEvent(event);
0150     QPainter p(this);
0151     for (auto window : m_stackingOrder) {
0152         KWin::SurfaceInterface *surface = window->surface();
0153         if (!surface || !surface->isMapped()) {
0154             continue;
0155         }
0156         KWin::GraphicsBufferView view(surface->buffer());
0157         if (view.image()) {
0158             p.drawImage(QPoint(0, 0), *view.image());
0159         }
0160         surface->frameRendered(QDateTime::currentMSecsSinceEpoch());
0161     }
0162 }
0163 
0164 void CompositorWindow::keyPressEvent(QKeyEvent *event)
0165 {
0166     QWidget::keyPressEvent(event);
0167     if (!m_seat) {
0168         return;
0169     }
0170     if (!m_seat->focusedKeyboardSurface()) {
0171         updateFocus();
0172     }
0173     m_seat->setTimestamp(std::chrono::milliseconds(event->timestamp()));
0174     m_seat->notifyKeyboardKey(event->nativeScanCode() - 8, KWin::KeyboardKeyState::Pressed);
0175 }
0176 
0177 void CompositorWindow::keyReleaseEvent(QKeyEvent *event)
0178 {
0179     QWidget::keyReleaseEvent(event);
0180     if (!m_seat) {
0181         return;
0182     }
0183     m_seat->setTimestamp(std::chrono::milliseconds(event->timestamp()));
0184     m_seat->notifyKeyboardKey(event->nativeScanCode() - 8, KWin::KeyboardKeyState::Released);
0185 }
0186 
0187 void CompositorWindow::mouseMoveEvent(QMouseEvent *event)
0188 {
0189     QWidget::mouseMoveEvent(event);
0190     if (!m_seat->focusedPointerSurface()) {
0191         updateFocus();
0192     }
0193     m_seat->setTimestamp(std::chrono::milliseconds(event->timestamp()));
0194     m_seat->notifyPointerMotion(event->localPos().toPoint());
0195     m_seat->notifyPointerFrame();
0196 }
0197 
0198 void CompositorWindow::mousePressEvent(QMouseEvent *event)
0199 {
0200     QWidget::mousePressEvent(event);
0201     if (!m_seat->focusedPointerSurface()) {
0202         if (!m_stackingOrder.isEmpty()) {
0203             m_seat->notifyPointerEnter(m_stackingOrder.last()->surface(), event->globalPos());
0204         }
0205     }
0206     m_seat->setTimestamp(std::chrono::milliseconds(event->timestamp()));
0207     m_seat->notifyPointerButton(event->button(), KWin::PointerButtonState::Pressed);
0208     m_seat->notifyPointerFrame();
0209 }
0210 
0211 void CompositorWindow::mouseReleaseEvent(QMouseEvent *event)
0212 {
0213     QWidget::mouseReleaseEvent(event);
0214     m_seat->setTimestamp(std::chrono::milliseconds(event->timestamp()));
0215     m_seat->notifyPointerButton(event->button(), KWin::PointerButtonState::Released);
0216     m_seat->notifyPointerFrame();
0217 }
0218 
0219 void CompositorWindow::wheelEvent(QWheelEvent *event)
0220 {
0221     QWidget::wheelEvent(event);
0222     m_seat->setTimestamp(std::chrono::milliseconds(event->timestamp()));
0223     const QPoint &angle = event->angleDelta() / (8 * 15);
0224     if (angle.x() != 0) {
0225         m_seat->notifyPointerAxis(Qt::Horizontal, angle.x(), 1, KWin::PointerAxisSource::Wheel);
0226     }
0227     if (angle.y() != 0) {
0228         m_seat->notifyPointerAxis(Qt::Vertical, angle.y(), 1, KWin::PointerAxisSource::Wheel);
0229     }
0230     m_seat->notifyPointerFrame();
0231 }
0232 
0233 int main(int argc, char **argv)
0234 {
0235     using namespace KWin;
0236     QApplication app(argc, argv);
0237 
0238     QCommandLineParser parser;
0239     parser.addHelpOption();
0240     QCommandLineOption xwaylandOption(QStringList{QStringLiteral("x"), QStringLiteral("xwayland")}, QStringLiteral("Start a rootless Xwayland server"));
0241     parser.addOption(xwaylandOption);
0242     parser.process(app);
0243 
0244     KWin::Display display;
0245     display.start();
0246     new DataDeviceManagerInterface(&display);
0247     new CompositorInterface(&display, &display);
0248     XdgShellInterface *shell = new XdgShellInterface(&display);
0249     display.createShm();
0250 
0251     const QSize windowSize(1024, 768);
0252 
0253     auto outputHandle = std::make_unique<FakeOutput>();
0254     outputHandle->setPhysicalSize(QSize(269, 202));
0255     outputHandle->setMode(windowSize, 60000);
0256 
0257     auto outputInterface = std::make_unique<OutputInterface>(&display, outputHandle.get());
0258 
0259     SeatInterface *seat = new SeatInterface(&display);
0260     seat->setHasKeyboard(true);
0261     seat->setHasPointer(true);
0262     seat->setName(QStringLiteral("testSeat0"));
0263 
0264     CompositorWindow compositorWindow;
0265     compositorWindow.setSeat(seat);
0266     compositorWindow.setMinimumSize(windowSize);
0267     compositorWindow.setMaximumSize(windowSize);
0268     compositorWindow.setGeometry(QRect(QPoint(0, 0), windowSize));
0269     compositorWindow.show();
0270     QObject::connect(shell, &XdgShellInterface::toplevelCreated, &compositorWindow, &CompositorWindow::surfaceCreated);
0271 
0272     // start XWayland
0273     if (parser.isSet(xwaylandOption)) {
0274         // starts XWayland by forking and opening a pipe
0275         const int pipe = startXServer();
0276         if (pipe == -1) {
0277             exit(1);
0278         }
0279 
0280         QThreadPool::globalInstance()->start([pipe] {
0281             readDisplayFromPipe(pipe);
0282         });
0283     }
0284 
0285     return app.exec();
0286 }
0287 
0288 #include "renderingservertest.moc"