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"