File indexing completed on 2024-05-19 16:33:11
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 #include "../src/server/buffer_interface.h" 0007 #include "../src/server/compositor_interface.h" 0008 #include "../src/server/datadevicemanager_interface.h" 0009 #include "../src/server/display.h" 0010 #include "../src/server/keyboard_interface.h" 0011 #include "../src/server/output_interface.h" 0012 #include "../src/server/pointer_interface.h" 0013 #include "../src/server/seat_interface.h" 0014 #include "../src/server/shell_interface.h" 0015 0016 #include <QApplication> 0017 #include <QCommandLineParser> 0018 #include <QDateTime> 0019 #include <QKeyEvent> 0020 #include <QMouseEvent> 0021 #include <QPainter> 0022 #include <QWidget> 0023 #include <QtConcurrentRun> 0024 0025 #include <iostream> 0026 #include <unistd.h> 0027 0028 static int startXServer() 0029 { 0030 const QByteArray process = QByteArrayLiteral("Xwayland"); 0031 int pipeFds[2]; 0032 if (pipe(pipeFds) != 0) { 0033 std::cerr << "FATAL ERROR failed to create pipe to start X Server " << process.constData() << std::endl; 0034 exit(1); 0035 } 0036 0037 pid_t pid = fork(); 0038 if (pid == 0) { 0039 // child process - should be turned into Xwayland 0040 // writes to pipe, closes read side 0041 close(pipeFds[0]); 0042 char fdbuf[16]; 0043 sprintf(fdbuf, "%d", pipeFds[1]); 0044 execlp(process.constData(), process.constData(), "-displayfd", fdbuf, "-rootless", (char *)nullptr); 0045 close(pipeFds[1]); 0046 exit(20); 0047 } 0048 // parent process - this is the wayland server 0049 // reads from pipe, closes write side 0050 close(pipeFds[1]); 0051 return pipeFds[0]; 0052 } 0053 0054 static void readDisplayFromPipe(int pipe) 0055 { 0056 QFile readPipe; 0057 if (!readPipe.open(pipe, QIODevice::ReadOnly)) { 0058 std::cerr << "FATAL ERROR failed to open pipe to start X Server XWayland" << std::endl; 0059 exit(1); 0060 } 0061 QByteArray displayNumber = readPipe.readLine(); 0062 0063 displayNumber.prepend(QByteArray(":")); 0064 displayNumber.remove(displayNumber.size() - 1, 1); 0065 std::cout << "X-Server started on display " << displayNumber.constData() << std::endl; 0066 0067 setenv("DISPLAY", displayNumber.constData(), true); 0068 0069 // close our pipe 0070 close(pipe); 0071 } 0072 0073 class CompositorWindow : public QWidget 0074 { 0075 Q_OBJECT 0076 public: 0077 explicit CompositorWindow(QWidget *parent = nullptr); 0078 ~CompositorWindow() override; 0079 0080 void surfaceCreated(KWayland::Server::ShellSurfaceInterface *surface); 0081 0082 void setSeat(const QPointer<KWayland::Server::SeatInterface> &seat); 0083 0084 protected: 0085 void paintEvent(QPaintEvent *event) override; 0086 void keyPressEvent(QKeyEvent *event) override; 0087 void keyReleaseEvent(QKeyEvent *event) override; 0088 void mouseMoveEvent(QMouseEvent *event) override; 0089 void mousePressEvent(QMouseEvent *event) override; 0090 void mouseReleaseEvent(QMouseEvent *event) override; 0091 void wheelEvent(QWheelEvent *event) override; 0092 0093 private: 0094 void updateFocus(); 0095 QList<KWayland::Server::ShellSurfaceInterface *> m_stackingOrder; 0096 QPointer<KWayland::Server::SeatInterface> m_seat; 0097 }; 0098 0099 CompositorWindow::CompositorWindow(QWidget *parent) 0100 : QWidget(parent) 0101 { 0102 setMouseTracking(true); 0103 } 0104 0105 CompositorWindow::~CompositorWindow() = default; 0106 0107 void CompositorWindow::surfaceCreated(KWayland::Server::ShellSurfaceInterface *surface) 0108 { 0109 using namespace KWayland::Server; 0110 m_stackingOrder << surface; 0111 connect(surface, &ShellSurfaceInterface::fullscreenChanged, this, [surface, this](bool fullscreen) { 0112 if (fullscreen) { 0113 surface->requestSize(size()); 0114 } 0115 }); 0116 connect(surface, &ShellSurfaceInterface::maximizedChanged, this, [surface, this](bool maximized) { 0117 if (maximized) { 0118 surface->requestSize(size()); 0119 } 0120 }); 0121 connect(surface->surface(), &SurfaceInterface::damaged, this, static_cast<void (CompositorWindow::*)()>(&CompositorWindow::update)); 0122 connect(surface, &ShellSurfaceInterface::destroyed, this, [surface, this] { 0123 m_stackingOrder.removeAll(surface); 0124 updateFocus(); 0125 update(); 0126 }); 0127 updateFocus(); 0128 } 0129 0130 void CompositorWindow::updateFocus() 0131 { 0132 using namespace KWayland::Server; 0133 if (!m_seat || m_stackingOrder.isEmpty()) { 0134 return; 0135 } 0136 auto it = std::find_if(m_stackingOrder.constBegin(), m_stackingOrder.constEnd(), [](ShellSurfaceInterface *s) { 0137 return s->surface()->buffer() != nullptr; 0138 }); 0139 if (it == m_stackingOrder.constEnd()) { 0140 return; 0141 } 0142 m_seat->setFocusedPointerSurface((*it)->surface()); 0143 m_seat->setFocusedKeyboardSurface((*it)->surface()); 0144 } 0145 0146 void CompositorWindow::setSeat(const QPointer<KWayland::Server::SeatInterface> &seat) 0147 { 0148 m_seat = seat; 0149 } 0150 0151 void CompositorWindow::paintEvent(QPaintEvent *event) 0152 { 0153 QWidget::paintEvent(event); 0154 QPainter p(this); 0155 for (auto s : m_stackingOrder) { 0156 if (auto *b = s->surface()->buffer()) { 0157 p.drawImage(QPoint(0, 0), b->data()); 0158 s->surface()->frameRendered(QDateTime::currentMSecsSinceEpoch()); 0159 } 0160 } 0161 } 0162 0163 void CompositorWindow::keyPressEvent(QKeyEvent *event) 0164 { 0165 QWidget::keyPressEvent(event); 0166 if (!m_seat) { 0167 return; 0168 } 0169 if (!m_seat->focusedKeyboardSurface()) { 0170 updateFocus(); 0171 } 0172 m_seat->setTimestamp(event->timestamp()); 0173 m_seat->keyPressed(event->nativeScanCode() - 8); 0174 } 0175 0176 void CompositorWindow::keyReleaseEvent(QKeyEvent *event) 0177 { 0178 QWidget::keyReleaseEvent(event); 0179 if (!m_seat) { 0180 return; 0181 } 0182 m_seat->setTimestamp(event->timestamp()); 0183 m_seat->keyReleased(event->nativeScanCode() - 8); 0184 } 0185 0186 void CompositorWindow::mouseMoveEvent(QMouseEvent *event) 0187 { 0188 QWidget::mouseMoveEvent(event); 0189 if (!m_seat->focusedPointerSurface()) { 0190 updateFocus(); 0191 } 0192 m_seat->setTimestamp(event->timestamp()); 0193 m_seat->setPointerPos(event->localPos().toPoint()); 0194 } 0195 0196 void CompositorWindow::mousePressEvent(QMouseEvent *event) 0197 { 0198 QWidget::mousePressEvent(event); 0199 if (!m_seat->focusedPointerSurface()) { 0200 if (!m_stackingOrder.isEmpty()) { 0201 m_seat->setFocusedPointerSurface(m_stackingOrder.last()->surface()); 0202 } 0203 } 0204 m_seat->setTimestamp(event->timestamp()); 0205 m_seat->pointerButtonPressed(event->button()); 0206 } 0207 0208 void CompositorWindow::mouseReleaseEvent(QMouseEvent *event) 0209 { 0210 QWidget::mouseReleaseEvent(event); 0211 m_seat->setTimestamp(event->timestamp()); 0212 m_seat->pointerButtonReleased(event->button()); 0213 } 0214 0215 void CompositorWindow::wheelEvent(QWheelEvent *event) 0216 { 0217 QWidget::wheelEvent(event); 0218 m_seat->setTimestamp(event->timestamp()); 0219 const QPoint &angle = event->angleDelta() / (8 * 15); 0220 if (angle.x() != 0) { 0221 m_seat->pointerAxis(Qt::Horizontal, angle.x()); 0222 } 0223 if (angle.y() != 0) { 0224 m_seat->pointerAxis(Qt::Vertical, angle.y()); 0225 } 0226 } 0227 0228 int main(int argc, char **argv) 0229 { 0230 using namespace KWayland::Server; 0231 QApplication app(argc, argv); 0232 0233 QCommandLineParser parser; 0234 parser.addHelpOption(); 0235 QCommandLineOption xwaylandOption(QStringList{QStringLiteral("x"), QStringLiteral("xwayland")}, QStringLiteral("Start a rootless Xwayland server")); 0236 parser.addOption(xwaylandOption); 0237 parser.process(app); 0238 0239 Display display; 0240 display.start(); 0241 DataDeviceManagerInterface *ddm = display.createDataDeviceManager(); 0242 ddm->create(); 0243 CompositorInterface *compositor = display.createCompositor(&display); 0244 compositor->create(); 0245 ShellInterface *shell = display.createShell(); 0246 shell->create(); 0247 display.createShm(); 0248 OutputInterface *output = display.createOutput(&display); 0249 output->setPhysicalSize(QSize(269, 202)); 0250 const QSize windowSize(1024, 768); 0251 output->addMode(windowSize); 0252 output->create(); 0253 SeatInterface *seat = display.createSeat(); 0254 seat->setHasKeyboard(true); 0255 seat->setHasPointer(true); 0256 seat->setName(QStringLiteral("testSeat0")); 0257 seat->create(); 0258 0259 CompositorWindow compositorWindow; 0260 compositorWindow.setSeat(seat); 0261 compositorWindow.setMinimumSize(windowSize); 0262 compositorWindow.setMaximumSize(windowSize); 0263 compositorWindow.setGeometry(QRect(QPoint(0, 0), windowSize)); 0264 compositorWindow.show(); 0265 QObject::connect(shell, &ShellInterface::surfaceCreated, &compositorWindow, &CompositorWindow::surfaceCreated); 0266 0267 // start XWayland 0268 if (parser.isSet(xwaylandOption)) { 0269 // starts XWayland by forking and opening a pipe 0270 const int pipe = startXServer(); 0271 if (pipe == -1) { 0272 exit(1); 0273 } 0274 0275 QtConcurrent::run([pipe] { 0276 readDisplayFromPipe(pipe); 0277 }); 0278 } 0279 0280 return app.exec(); 0281 } 0282 0283 #include "renderingservertest.moc"