diff --git a/src/client/shm_pool.cpp b/src/client/shm_pool.cpp index 9835ba0..bbe5d1b 100644 --- a/src/client/shm_pool.cpp +++ b/src/client/shm_pool.cpp @@ -1,283 +1,283 @@ /******************************************************************** Copyright 2013 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "shm_pool.h" #include "event_queue.h" #include "buffer.h" #include "buffer_p.h" #include "logging_p.h" #include "wayland_pointer_p.h" // Qt #include #include #include // system #include #include // wayland #include namespace KWayland { namespace Client { class ShmPool::Private { public: Private(ShmPool *q); bool createPool(); bool resizePool(int32_t newSize); QList>::iterator getBuffer(const QSize &size, int32_t stride, Buffer::Format format); WaylandPointer shm; WaylandPointer pool; void *poolData = nullptr; int32_t size = 1024; QScopedPointer tmpFile; bool valid = false; int offset = 0; QList> buffers; EventQueue *queue = nullptr; private: ShmPool *q; }; ShmPool::Private::Private(ShmPool *q) : tmpFile(new QTemporaryFile()) , q(q) { } ShmPool::ShmPool(QObject *parent) : QObject(parent) , d(new Private(this)) { } ShmPool::~ShmPool() { release(); } void ShmPool::release() { d->buffers.clear(); if (d->poolData) { munmap(d->poolData, d->size); d->poolData = nullptr; } d->pool.release(); d->shm.release(); d->tmpFile->close(); d->valid = false; d->offset = 0; } void ShmPool::destroy() { for (auto b : d->buffers) { b->d->destroy(); } d->buffers.clear(); if (d->poolData) { munmap(d->poolData, d->size); d->poolData = nullptr; } d->pool.destroy(); d->shm.destroy(); d->tmpFile->close(); d->valid = false; d->offset = 0; } void ShmPool::setup(wl_shm *shm) { Q_ASSERT(shm); Q_ASSERT(!d->shm); d->shm.setup(shm); d->valid = d->createPool(); } void ShmPool::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *ShmPool::eventQueue() { return d->queue; } bool ShmPool::Private::createPool() { if (!tmpFile->open()) { qCDebug(KWAYLAND_CLIENT) << "Could not open temporary file for Shm pool"; return false; } if (unlink(tmpFile->fileName().toUtf8().constData()) != 0) { qCDebug(KWAYLAND_CLIENT) << "Unlinking temporary file for Shm pool from file system failed"; } if (ftruncate(tmpFile->handle(), size) < 0) { qCDebug(KWAYLAND_CLIENT) << "Could not set size for Shm pool file"; return false; } - poolData = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, tmpFile->handle(), 0); + poolData = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, tmpFile->handle(), 0); pool.setup(wl_shm_create_pool(shm, tmpFile->handle(), size)); if (!poolData || !pool) { qCDebug(KWAYLAND_CLIENT) << "Creating Shm pool failed"; return false; } return true; } bool ShmPool::Private::resizePool(int32_t newSize) { if (ftruncate(tmpFile->handle(), newSize) < 0) { qCDebug(KWAYLAND_CLIENT) << "Could not set new size for Shm pool file"; return false; } wl_shm_pool_resize(pool, newSize); munmap(poolData, size); - poolData = mmap(NULL, newSize, PROT_READ | PROT_WRITE, MAP_SHARED, tmpFile->handle(), 0); + poolData = mmap(nullptr, newSize, PROT_READ | PROT_WRITE, MAP_SHARED, tmpFile->handle(), 0); size = newSize; if (!poolData) { qCDebug(KWAYLAND_CLIENT) << "Resizing Shm pool failed"; return false; } emit q->poolResized(); return true; } namespace { static Buffer::Format toBufferFormat(const QImage &image) { switch (image.format()) { case QImage::Format_ARGB32: case QImage::Format_ARGB32_Premultiplied: return Buffer::Format::ARGB32; case QImage::Format_RGB32: return Buffer::Format::RGB32; default: qCWarning(KWAYLAND_CLIENT) << "Unsupported image format: " << image.format() << "going to use ARGB32, expect rendering errors"; return Buffer::Format::ARGB32; } } } Buffer::Ptr ShmPool::createBuffer(const QImage& image) { if (image.isNull() || !d->valid) { return QWeakPointer(); } auto it = d->getBuffer(image.size(), image.bytesPerLine(), toBufferFormat(image)); if (it == d->buffers.end()) { return QWeakPointer(); } (*it)->copy(image.bits()); return QWeakPointer(*it); } Buffer::Ptr ShmPool::createBuffer(const QSize &size, int32_t stride, const void *src, Buffer::Format format) { if (size.isEmpty() || !d->valid) { return QWeakPointer(); } auto it = d->getBuffer(size, stride, format); if (it == d->buffers.end()) { return QWeakPointer(); } (*it)->copy(src); return QWeakPointer(*it); } namespace { static wl_shm_format toWaylandFormat(Buffer::Format format) { switch (format) { case Buffer::Format::ARGB32: return WL_SHM_FORMAT_ARGB8888; case Buffer::Format::RGB32: return WL_SHM_FORMAT_XRGB8888; } abort(); } } Buffer::Ptr ShmPool::getBuffer(const QSize &size, int32_t stride, Buffer::Format format) { auto it = d->getBuffer(size, stride, format); if (it == d->buffers.end()) { return QWeakPointer(); } return QWeakPointer(*it); } QList>::iterator ShmPool::Private::getBuffer(const QSize &s, int32_t stride, Buffer::Format format) { for (auto it = buffers.begin(); it != buffers.end(); ++it) { auto buffer = *it; if (!buffer->isReleased() || buffer->isUsed()) { continue; } if (buffer->size() != s || buffer->stride() != stride || buffer->format() != format) { continue; } buffer->setReleased(false); return it; } const int32_t byteCount = s.height() * stride; if (offset + byteCount > size) { if (!resizePool(size + byteCount)) { return buffers.end(); } } // we don't have a buffer which we could reuse - need to create a new one wl_buffer *native = wl_shm_pool_create_buffer(pool, offset, s.width(), s.height(), stride, toWaylandFormat(format)); if (!native) { return buffers.end(); } if (queue) { queue->addProxy(native); } Buffer *buffer = new Buffer(q, native, s, stride, offset, format); offset += byteCount; auto it = buffers.insert(buffers.end(), QSharedPointer(buffer)); return it; } bool ShmPool::isValid() const { return d->valid; } void* ShmPool::poolAddress() const { return d->poolData; } wl_shm *ShmPool::shm() { return d->shm; } } } diff --git a/tests/renderingservertest.cpp b/tests/renderingservertest.cpp index 82aaa2f..5ae0edb 100644 --- a/tests/renderingservertest.cpp +++ b/tests/renderingservertest.cpp @@ -1,306 +1,306 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "../src/server/buffer_interface.h" #include "../src/server/compositor_interface.h" #include "../src/server/datadevicemanager_interface.h" #include "../src/server/display.h" #include "../src/server/keyboard_interface.h" #include "../src/server/output_interface.h" #include "../src/server/pointer_interface.h" #include "../src/server/seat_interface.h" #include "../src/server/shell_interface.h" #include #include #include #include #include #include #include #include #include #include static int startXServer() { const QByteArray process = QByteArrayLiteral("Xwayland"); int pipeFds[2]; if (pipe(pipeFds) != 0) { std::cerr << "FATAL ERROR failed to create pipe to start X Server " << process.constData() << std::endl; exit(1); } pid_t pid = fork(); if (pid == 0) { // child process - should be turned into Xwayland // writes to pipe, closes read side close(pipeFds[0]); char fdbuf[16]; sprintf(fdbuf, "%d", pipeFds[1]); - execlp(process.constData(), process.constData(), "-displayfd", fdbuf, "-rootless", (char *)0); + execlp(process.constData(), process.constData(), "-displayfd", fdbuf, "-rootless", (char *)nullptr); close(pipeFds[1]); exit(20); } // parent process - this is the wayland server // reads from pipe, closes write side close(pipeFds[1]); return pipeFds[0]; } static void readDisplayFromPipe(int pipe) { QFile readPipe; if (!readPipe.open(pipe, QIODevice::ReadOnly)) { std::cerr << "FATAL ERROR failed to open pipe to start X Server XWayland" << std::endl; exit(1); } QByteArray displayNumber = readPipe.readLine(); displayNumber.prepend(QByteArray(":")); displayNumber.remove(displayNumber.size() -1, 1); std::cout << "X-Server started on display " << displayNumber.constData() << std::endl; setenv("DISPLAY", displayNumber.constData(), true); // close our pipe close(pipe); } class CompositorWindow : public QWidget { Q_OBJECT public: explicit CompositorWindow(QWidget *parent = nullptr); virtual ~CompositorWindow(); void surfaceCreated(KWayland::Server::ShellSurfaceInterface *surface); void setSeat(const QPointer &seat); protected: void paintEvent(QPaintEvent *event) override; void keyPressEvent(QKeyEvent *event) override; void keyReleaseEvent(QKeyEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; private: void updateFocus(); QList m_stackingOrder; QPointer m_seat; }; CompositorWindow::CompositorWindow(QWidget *parent) : QWidget(parent) { setMouseTracking(true); } CompositorWindow::~CompositorWindow() = default; void CompositorWindow::surfaceCreated(KWayland::Server::ShellSurfaceInterface *surface) { using namespace KWayland::Server; m_stackingOrder << surface; connect(surface, &ShellSurfaceInterface::fullscreenChanged, this, [surface, this](bool fullscreen) { if (fullscreen) { surface->requestSize(size()); } } ); connect(surface, &ShellSurfaceInterface::maximizedChanged, this, [surface, this](bool maximized) { if (maximized) { surface->requestSize(size()); } } ); connect(surface->surface(), &SurfaceInterface::damaged, this, static_cast(&CompositorWindow::update)); connect(surface, &ShellSurfaceInterface::destroyed, this, [surface, this] { m_stackingOrder.removeAll(surface); updateFocus(); update(); } ); updateFocus(); } void CompositorWindow::updateFocus() { using namespace KWayland::Server; if (!m_seat || m_stackingOrder.isEmpty()) { return; } auto it = std::find_if(m_stackingOrder.constBegin(), m_stackingOrder.constEnd(), [](ShellSurfaceInterface *s) { return s->surface()->buffer() != nullptr; } ); if (it == m_stackingOrder.constEnd()) { return; } m_seat->setFocusedPointerSurface((*it)->surface()); m_seat->setFocusedKeyboardSurface((*it)->surface()); } void CompositorWindow::setSeat(const QPointer< KWayland::Server::SeatInterface > &seat) { m_seat = seat; } void CompositorWindow::paintEvent(QPaintEvent *event) { QWidget::paintEvent(event); QPainter p(this); for (auto s : m_stackingOrder) { if (auto *b = s->surface()->buffer()) { p.drawImage(QPoint(0, 0), b->data()); s->surface()->frameRendered(QDateTime::currentMSecsSinceEpoch()); } } } void CompositorWindow::keyPressEvent(QKeyEvent *event) { QWidget::keyPressEvent(event); if (!m_seat) { return; } if (!m_seat->focusedKeyboardSurface()) { updateFocus(); } m_seat->setTimestamp(event->timestamp()); m_seat->keyPressed(event->nativeScanCode() - 8); } void CompositorWindow::keyReleaseEvent(QKeyEvent *event) { QWidget::keyReleaseEvent(event); if (!m_seat) { return; } m_seat->setTimestamp(event->timestamp()); m_seat->keyReleased(event->nativeScanCode() - 8); } void CompositorWindow::mouseMoveEvent(QMouseEvent *event) { QWidget::mouseMoveEvent(event); if (!m_seat->focusedPointerSurface()) { updateFocus(); } m_seat->setTimestamp(event->timestamp()); m_seat->setPointerPos(event->localPos().toPoint()); } void CompositorWindow::mousePressEvent(QMouseEvent *event) { QWidget::mousePressEvent(event); if (!m_seat->focusedPointerSurface()) { if (!m_stackingOrder.isEmpty()) { m_seat->setFocusedPointerSurface(m_stackingOrder.last()->surface()); } } m_seat->setTimestamp(event->timestamp()); m_seat->pointerButtonPressed(event->button()); } void CompositorWindow::mouseReleaseEvent(QMouseEvent *event) { QWidget::mouseReleaseEvent(event); m_seat->setTimestamp(event->timestamp()); m_seat->pointerButtonReleased(event->button()); } void CompositorWindow::wheelEvent(QWheelEvent *event) { QWidget::wheelEvent(event); m_seat->setTimestamp(event->timestamp()); const QPoint &angle = event->angleDelta() / (8 * 15); if (angle.x() != 0) { m_seat->pointerAxis(Qt::Horizontal, angle.x()); } if (angle.y() != 0) { m_seat->pointerAxis(Qt::Vertical, angle.y()); } } int main(int argc, char **argv) { using namespace KWayland::Server; QApplication app(argc, argv); QCommandLineParser parser; parser.addHelpOption(); QCommandLineOption xwaylandOption(QStringList{QStringLiteral("x"), QStringLiteral("xwayland")}, QStringLiteral("Start a rootless Xwayland server")); parser.addOption(xwaylandOption); parser.process(app); Display display; display.start(); DataDeviceManagerInterface *ddm = display.createDataDeviceManager(); ddm->create(); CompositorInterface *compositor = display.createCompositor(&display); compositor->create(); ShellInterface *shell = display.createShell(); shell->create(); display.createShm(); OutputInterface *output = display.createOutput(&display); output->setPhysicalSize(QSize(269, 202)); const QSize windowSize(1024, 768); output->addMode(windowSize); output->create(); SeatInterface *seat = display.createSeat(); seat->setHasKeyboard(true); seat->setHasPointer(true); seat->setName(QStringLiteral("testSeat0")); seat->create(); CompositorWindow compositorWindow; compositorWindow.setSeat(seat); compositorWindow.setMinimumSize(windowSize); compositorWindow.setMaximumSize(windowSize); compositorWindow.setGeometry(QRect(QPoint(0, 0), windowSize)); compositorWindow.show(); QObject::connect(shell, &ShellInterface::surfaceCreated, &compositorWindow, &CompositorWindow::surfaceCreated); // start XWayland if (parser.isSet(xwaylandOption)) { // starts XWayland by forking and opening a pipe const int pipe = startXServer(); if (pipe == -1) { exit(1); } QtConcurrent::run([pipe] { readDisplayFromPipe(pipe); }); } return app.exec(); } #include "renderingservertest.moc" diff --git a/tests/waylandservertest.cpp b/tests/waylandservertest.cpp index 29ae90e..b56736c 100644 --- a/tests/waylandservertest.cpp +++ b/tests/waylandservertest.cpp @@ -1,127 +1,127 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "../src/server/compositor_interface.h" #include "../src/server/display.h" #include "../src/server/output_interface.h" #include "../src/server/seat_interface.h" #include "../src/server/shell_interface.h" #include #include #include #include #include static int startXServer() { const QByteArray process = QByteArrayLiteral("Xwayland"); int pipeFds[2]; if (pipe(pipeFds) != 0) { std::cerr << "FATAL ERROR failed to create pipe to start X Server " << process.constData() << std::endl; exit(1); } pid_t pid = fork(); if (pid == 0) { // child process - should be turned into Xwayland // writes to pipe, closes read side close(pipeFds[0]); char fdbuf[16]; sprintf(fdbuf, "%d", pipeFds[1]); - execlp(process.constData(), process.constData(), "-displayfd", fdbuf, (char *)0); + execlp(process.constData(), process.constData(), "-displayfd", fdbuf, (char *)nullptr); close(pipeFds[1]); exit(20); } // parent process - this is the wayland server // reads from pipe, closes write side close(pipeFds[1]); return pipeFds[0]; } static void readDisplayFromPipe(int pipe) { QFile readPipe; if (!readPipe.open(pipe, QIODevice::ReadOnly)) { std::cerr << "FATAL ERROR failed to open pipe to start X Server XWayland" << std::endl; exit(1); } QByteArray displayNumber = readPipe.readLine(); displayNumber.prepend(QByteArray(":")); displayNumber.remove(displayNumber.size() -1, 1); std::cout << "X-Server started on display " << displayNumber.constData() << std::endl; setenv("DISPLAY", displayNumber.constData(), true); // close our pipe close(pipe); } int main(int argc, char **argv) { using namespace KWayland::Server; // set our own event dispatcher to be able to dispatch events before the event loop is started QAbstractEventDispatcher *eventDispatcher = new QEventDispatcherGlib(); QCoreApplication::setEventDispatcher(eventDispatcher); // first create the Server and setup with minimum to get an XWayland connected Display display; display.start(); display.createShm(); CompositorInterface *compositor = display.createCompositor(&display); compositor->create(); ShellInterface *shell = display.createShell(); shell->create(); OutputInterface *output = display.createOutput(&display); output->setPhysicalSize(QSize(10, 10)); output->addMode(QSize(1024, 768)); output->create(); // starts XWayland by forking and opening a pipe const int pipe = startXServer(); if (pipe == -1) { exit(1); } fd_set rfds; struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; do { eventDispatcher->processEvents(QEventLoop::WaitForMoreEvents); FD_ZERO(&rfds); FD_SET(pipe, &rfds); - } while (select(pipe + 1, &rfds, NULL, NULL, &tv) == 0); + } while (select(pipe + 1, &rfds, nullptr, nullptr, &tv) == 0); // now Xwayland is ready and we can read the pipe to get the display readDisplayFromPipe(pipe); QGuiApplication app(argc, argv); SeatInterface *seat = display.createSeat(); seat->setName(QStringLiteral("testSeat0")); seat->create(); return app.exec(); }