From 7ee4be6af2c92c345539bb4950dd48d7a740847d Mon Sep 17 00:00:00 2001 From: Pier Luigi Fiorini Date: Fri, 10 Apr 2015 17:52:49 +0200 Subject: Add mode support to QWaylandOutput Outputs usually have more than one mode, add an API to support them. When sizeFollowsWindow is true, modes are replaced by one with the window size and refresh rate. In that circumstance the mode changes when the window is resized. The sizeFollowsWindow property default value is no longer true. The setGeometry() method is gone as it doesn't make sense now, the setWidth() and setHeight() methods are now private slots to resize the resolution as the window resizes (and sizeFollowsWindow is true). Refresh rate is expressed in mHz rather than Hz just like the Wayland protocol. A compositor implementation may choose to add modes if it has access to hardware information, it will call addMode() for each mode and then invoke the setCurrentMode() method that sends the modes list to the client. The preferred mode is indicated with a boolean parameter to the addMode() method. Change-Id: Iffed4784ccef695c276ebd800172957f4cff3324 Task-number: QTBUG-49814 Reviewed-by: Paul Olav Tvete Reviewed-by: Johan Helsing --- .../custom-extension/compositor/qml/Screen.qml | 1 + examples/wayland/minimal-cpp/compositor.cpp | 5 +- examples/wayland/minimal-qml/main.qml | 1 + examples/wayland/multi-output/qml/GridScreen.qml | 1 + examples/wayland/multi-output/qml/ShellScreen.qml | 1 + examples/wayland/pure-qml/qml/Screen.qml | 1 + examples/wayland/qwindow-compositor/compositor.cpp | 5 +- examples/wayland/server-buffer/compositor/main.cpp | 9 +- examples/wayland/spanning-screens/main.qml | 2 + src/compositor/compositor_api/compositor_api.pri | 6 +- src/compositor/compositor_api/qwaylandoutput.cpp | 269 +++++++++++++-------- src/compositor/compositor_api/qwaylandoutput.h | 28 +-- src/compositor/compositor_api/qwaylandoutput_p.h | 12 +- .../compositor_api/qwaylandoutputmode.cpp | 139 +++++++++++ src/compositor/compositor_api/qwaylandoutputmode.h | 72 ++++++ .../compositor_api/qwaylandoutputmode_p.h | 66 +++++ tests/auto/compositor/compositor/mockclient.cpp | 19 +- tests/auto/compositor/compositor/mockclient.h | 8 +- .../auto/compositor/compositor/tst_compositor.cpp | 58 ++++- 19 files changed, 565 insertions(+), 138 deletions(-) create mode 100644 src/compositor/compositor_api/qwaylandoutputmode.cpp create mode 100644 src/compositor/compositor_api/qwaylandoutputmode.h create mode 100644 src/compositor/compositor_api/qwaylandoutputmode_p.h diff --git a/examples/wayland/custom-extension/compositor/qml/Screen.qml b/examples/wayland/custom-extension/compositor/qml/Screen.qml index a6d5fbc7c..7a87951b7 100644 --- a/examples/wayland/custom-extension/compositor/qml/Screen.qml +++ b/examples/wayland/custom-extension/compositor/qml/Screen.qml @@ -45,6 +45,7 @@ import QtWayland.Compositor 1.0 WaylandOutput { id: output property alias surfaceArea: background + sizeFollowsWindow: true window: Window { id: screen diff --git a/examples/wayland/minimal-cpp/compositor.cpp b/examples/wayland/minimal-cpp/compositor.cpp index 5e46895c4..5a6249c9a 100644 --- a/examples/wayland/minimal-cpp/compositor.cpp +++ b/examples/wayland/minimal-cpp/compositor.cpp @@ -67,8 +67,11 @@ Compositor::~Compositor() void Compositor::create() { - new QWaylandOutput(this, m_window); + QWaylandOutput *output = new QWaylandOutput(this, m_window); + QWaylandOutputMode mode(QSize(800, 600), 60000); + output->addMode(mode, true); QWaylandCompositor::create(); + output->setCurrentMode(mode); connect(this, &QWaylandCompositor::surfaceCreated, this, &Compositor::onSurfaceCreated); } diff --git a/examples/wayland/minimal-qml/main.qml b/examples/wayland/minimal-qml/main.qml index 3d6c3b7bb..d44d0c6a1 100644 --- a/examples/wayland/minimal-qml/main.qml +++ b/examples/wayland/minimal-qml/main.qml @@ -47,6 +47,7 @@ WaylandCompositor { // The output defines the screen. WaylandOutput { compositor: wlcompositor + sizeFollowsWindow: true window: Window { width: 1024 height: 768 diff --git a/examples/wayland/multi-output/qml/GridScreen.qml b/examples/wayland/multi-output/qml/GridScreen.qml index 3dab99d0a..2a48cf169 100644 --- a/examples/wayland/multi-output/qml/GridScreen.qml +++ b/examples/wayland/multi-output/qml/GridScreen.qml @@ -46,6 +46,7 @@ WaylandOutput { id: output property alias gridSurfaces: listModel + sizeFollowsWindow: true window: Window { width: 1024 height: 760 diff --git a/examples/wayland/multi-output/qml/ShellScreen.qml b/examples/wayland/multi-output/qml/ShellScreen.qml index 9a6122118..7b8a48ff3 100644 --- a/examples/wayland/multi-output/qml/ShellScreen.qml +++ b/examples/wayland/multi-output/qml/ShellScreen.qml @@ -46,6 +46,7 @@ WaylandOutput { id: output property alias surfaceArea: background + sizeFollowsWindow: true window: Window { width: 1024 height: 760 diff --git a/examples/wayland/pure-qml/qml/Screen.qml b/examples/wayland/pure-qml/qml/Screen.qml index 0920a8b95..a12f387fd 100644 --- a/examples/wayland/pure-qml/qml/Screen.qml +++ b/examples/wayland/pure-qml/qml/Screen.qml @@ -45,6 +45,7 @@ import QtWayland.Compositor 1.0 WaylandOutput { id: output property alias surfaceArea: background + sizeFollowsWindow: true window: Window { id: screen diff --git a/examples/wayland/qwindow-compositor/compositor.cpp b/examples/wayland/qwindow-compositor/compositor.cpp index a55bb3b72..4878c373e 100644 --- a/examples/wayland/qwindow-compositor/compositor.cpp +++ b/examples/wayland/qwindow-compositor/compositor.cpp @@ -139,8 +139,11 @@ Compositor::~Compositor() void Compositor::create() { - new QWaylandOutput(this, m_window); + QWaylandOutput *output = new QWaylandOutput(this, m_window); + QWaylandOutputMode mode(QSize(800, 600), 60000); + output->addMode(mode, true); QWaylandCompositor::create(); + output->setCurrentMode(mode); connect(this, &QWaylandCompositor::surfaceCreated, this, &Compositor::onSurfaceCreated); connect(defaultSeat(), &QWaylandSeat::cursorSurfaceRequest, this, &Compositor::adjustCursorSurface); diff --git a/examples/wayland/server-buffer/compositor/main.cpp b/examples/wayland/server-buffer/compositor/main.cpp index 9c5ee42a1..8c43ce5c5 100644 --- a/examples/wayland/server-buffer/compositor/main.cpp +++ b/examples/wayland/server-buffer/compositor/main.cpp @@ -86,15 +86,13 @@ public: m_view.setColor(Qt::black); m_view.create(); m_output = new QWaylandQuickOutput(this, &m_view); + m_output->setSizeFollowsWindow(true); connect(&m_view, &QQuickView::afterRendering, this, &QmlCompositor::sendCallbacks); connect(&m_view, &QQuickView::sceneGraphInitialized, this, &QmlCompositor::initiateServerBuffer,Qt::DirectConnection); connect(this, &QmlCompositor::serverBuffersCreated, this, &QmlCompositor::createServerBufferItems); - connect(&m_view, &QWindow::widthChanged, this, &QmlCompositor::sizeAdjusted); - connect(&m_view, &QWindow::heightChanged, this, &QmlCompositor::sizeAdjusted); - connect(this, SIGNAL(windowAdded(QVariant)), m_view.rootObject(), SLOT(windowAdded(QVariant))); connect(this, SIGNAL(windowResized(QVariant)), m_view.rootObject(), SLOT(windowResized(QVariant))); connect(this, SIGNAL(serverBufferItemCreated(QVariant)), m_view.rootObject(), SLOT(serverBufferItemCreated(QVariant))); @@ -211,11 +209,6 @@ private slots: } } protected: - void sizeAdjusted() - { - defaultOutput()->setGeometry(QRect(QPoint(0, 0), m_view.size())); - } - void onSurfaceCreated(QWaylandSurface *surface) { QWaylandQuickItem *item = new QWaylandQuickItem(); item->setSurface(surface); diff --git a/examples/wayland/spanning-screens/main.qml b/examples/wayland/spanning-screens/main.qml index 00bf517fc..32dc11f33 100644 --- a/examples/wayland/spanning-screens/main.qml +++ b/examples/wayland/spanning-screens/main.qml @@ -47,6 +47,7 @@ WaylandCompositor { WaylandOutput { compositor: wlcompositor + sizeFollowsWindow: true window: Window { id: topSurfaceArea width: 1024 @@ -59,6 +60,7 @@ WaylandCompositor { WaylandOutput { compositor: wlcompositor + sizeFollowsWindow: true window: Window { id: bottomSurfaceArea width: 1024 diff --git a/src/compositor/compositor_api/compositor_api.pri b/src/compositor/compositor_api/compositor_api.pri index 0f70773cb..0253cd0ee 100644 --- a/src/compositor/compositor_api/compositor_api.pri +++ b/src/compositor/compositor_api/compositor_api.pri @@ -17,6 +17,8 @@ HEADERS += \ compositor_api/qwaylandtouch.h \ compositor_api/qwaylandtouch_p.h \ compositor_api/qwaylandoutput.h \ + compositor_api/qwaylandoutputmode.h \ + compositor_api/qwaylandoutputmode_p.h \ compositor_api/qwaylanddrag.h \ compositor_api/qwaylandbufferref.h \ compositor_api/qwaylanddestroylistener.h \ @@ -26,7 +28,8 @@ HEADERS += \ compositor_api/qwaylandresource.h \ compositor_api/qwaylandsurfacegrabber.h \ compositor_api/qwaylandinputmethodcontrol.h \ - compositor_api/qwaylandinputmethodcontrol_p.h + compositor_api/qwaylandinputmethodcontrol_p.h \ + compositor_api/qwaylandoutputmode_p.h SOURCES += \ compositor_api/qwaylandcompositor.cpp \ @@ -38,6 +41,7 @@ SOURCES += \ compositor_api/qwaylandpointer.cpp \ compositor_api/qwaylandtouch.cpp \ compositor_api/qwaylandoutput.cpp \ + compositor_api/qwaylandoutputmode.cpp \ compositor_api/qwaylanddrag.cpp \ compositor_api/qwaylandbufferref.cpp \ compositor_api/qwaylanddestroylistener.cpp \ diff --git a/src/compositor/compositor_api/qwaylandoutput.cpp b/src/compositor/compositor_api/qwaylandoutput.cpp index 40cd9798c..19e17bb32 100644 --- a/src/compositor/compositor_api/qwaylandoutput.cpp +++ b/src/compositor/compositor_api/qwaylandoutput.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2014-2015 Pier Luigi Fiorini +** Copyright (C) 2014-2016 Pier Luigi Fiorini ** Copyright (C) 2013 Klarälvdalens Datakonsult AB (KDAB). ** Contact: http://www.qt.io/licensing/ ** @@ -48,6 +48,7 @@ #include #include #include +#include #include QT_BEGIN_NAMESPACE @@ -102,16 +103,14 @@ QWaylandOutputPrivate::QWaylandOutputPrivate() : QtWaylandServer::wl_output() , compositor(Q_NULLPTR) , window(Q_NULLPTR) + , currentMode(-1) + , preferredMode(-1) , subpixel(QWaylandOutput::SubpixelUnknown) , transform(QWaylandOutput::TransformNormal) , scaleFactor(1) - , sizeFollowsWindow(true) + , sizeFollowsWindow(false) , initialized(false) { - mode.size = QSize(); - mode.refreshRate = 60; - - qRegisterMetaType("WaylandOutput::Mode"); } QWaylandOutputPrivate::~QWaylandOutputPrivate() @@ -120,15 +119,10 @@ QWaylandOutputPrivate::~QWaylandOutputPrivate() void QWaylandOutputPrivate::output_bind_resource(Resource *resource) { - send_geometry(resource->handle, - position.x(), position.y(), - physicalSize.width(), physicalSize.height(), - toWlSubpixel(subpixel), manufacturer, model, - toWlTransform(transform)); + sendGeometry(resource); - send_mode(resource->handle, mode_current | mode_preferred, - mode.size.width(), mode.size.height(), - mode.refreshRate * 1000); + for (const QWaylandOutputMode &mode : modes) + sendMode(resource, mode); if (resource->version() >= 2) { send_scale(resource->handle, scaleFactor); @@ -136,19 +130,46 @@ void QWaylandOutputPrivate::output_bind_resource(Resource *resource) } } +void QWaylandOutputPrivate::sendGeometry(const Resource *resource) +{ + send_geometry(resource->handle, + position.x(), position.y(), + physicalSize.width(), physicalSize.height(), + toWlSubpixel(subpixel), manufacturer, model, + toWlTransform(transform)); +} + void QWaylandOutputPrivate::sendGeometryInfo() { - Q_FOREACH (Resource *resource, resourceMap().values()) { - send_geometry(resource->handle, - position.x(), position.y(), - physicalSize.width(), physicalSize.height(), - toWlSubpixel(subpixel), manufacturer, model, - toWlTransform(transform)); + for (const Resource *resource : resourceMap().values()) { + sendGeometry(resource); if (resource->version() >= 2) send_done(resource->handle); } } +void QWaylandOutputPrivate::sendMode(const Resource *resource, const QWaylandOutputMode &mode) +{ + quint32 flags = 0; + if (currentMode == modes.indexOf(mode)) + flags |= QtWaylandServer::wl_output::mode_current; + if (preferredMode == modes.indexOf(mode)) + flags |= QtWaylandServer::wl_output::mode_preferred; + + send_mode(resource->handle, flags, + mode.size().width(), mode.size().height(), + mode.refreshRate()); +} + +void QWaylandOutputPrivate::sendModesInfo() +{ + for (const Resource *resource : resourceMap().values()) { + for (const QWaylandOutputMode &mode : modes) + sendMode(resource, mode); + if (resource->version() >= 2) + send_done(resource->handle); + } +} void QWaylandOutputPrivate::addView(QWaylandView *view, QWaylandSurface *surface) { @@ -252,16 +273,23 @@ void QWaylandOutput::initialize() Q_ASSERT(d->compositor); Q_ASSERT(d->compositor->isCreated()); - if (d->window) - d->mode.size = d->window->size(); - else - d->sizeFollowsWindow = false; + // Replace modes with one that follows the window size and refresh rate, + // but only if window size is valid + if (d->window && d->sizeFollowsWindow) { + QWaylandOutputMode mode(d->window->size(), + qFloor(d->window->screen()->refreshRate() * 1000)); + if (mode.isValid()) { + d->modes.clear(); + addMode(mode, true); + setCurrentMode(mode); + } + } QWaylandCompositorPrivate::get(d->compositor)->addOutput(this); if (d->window) { - QObject::connect(d->window, &QWindow::widthChanged, this, &QWaylandOutput::setWidth); - QObject::connect(d->window, &QWindow::heightChanged, this, &QWaylandOutput::setHeight); + QObject::connect(d->window, &QWindow::widthChanged, this, &QWaylandOutput::handleSetWidth); + QObject::connect(d->window, &QWindow::heightChanged, this, &QWaylandOutput::handleSetHeight); QObject::connect(d->window, &QObject::destroyed, this, &QWaylandOutput::handleWindowDestroyed); } @@ -431,39 +459,75 @@ void QWaylandOutput::setPosition(const QPoint &pt) } /*! - * \property QWaylandOutput::mode - * - * This property holds the output's size in pixels and refresh rate in Hz. + * Returns the list of modes. */ -QWaylandOutput::Mode QWaylandOutput::mode() const +QList QWaylandOutput::modes() const { - return d_func()->mode; + Q_D(const QWaylandOutput); + return d->modes.toList(); } -void QWaylandOutput::setMode(const Mode &mode) +/*! + * Adds the mode \a mode to the output and mark it as preferred + * if \a preferred is \c true. + * Please note there can only be one preferred mode. + */ +void QWaylandOutput::addMode(const QWaylandOutputMode &mode, bool preferred) { Q_D(QWaylandOutput); - if (d->mode.size == mode.size && d->mode.refreshRate == mode.refreshRate) + + if (!mode.isValid()) { + qWarning("Cannot add an invalid mode"); return; + } - d->mode = mode; + d->modes.append(mode); - Q_FOREACH (QWaylandOutputPrivate::Resource *resource, d->resourceMap().values()) { - d->send_mode(resource->handle, d->mode_current, - d->mode.size.width(), d->mode.size.height(), - d->mode.refreshRate * 1000); - if (resource->version() >= 2) - d->send_done(resource->handle); + if (preferred) + d->preferredMode = d->modes.indexOf(mode); + + emit modeAdded(); +} + +/*! + * Returns the output's size in pixels and refresh rate in mHz. + * If the current mode is not set it will return an invalid mode. + * + * \sa QWaylandOutput::modes + * \sa QWaylandOutputMode + */ +QWaylandOutputMode QWaylandOutput::currentMode() const +{ + Q_D(const QWaylandOutput); + + if (d->currentMode >= 0 && d->currentMode <= d->modes.size() - 1) + return d->modes.at(d->currentMode); + return QWaylandOutputMode(); +} + +/*! + * Sets the current mode. + * The mode \a mode must have been previously added. + * + * \sa QWaylandOutput::modes + * \sa QWaylandOutputMode + */ +void QWaylandOutput::setCurrentMode(const QWaylandOutputMode &mode) +{ + Q_D(QWaylandOutput); + + int index = d->modes.indexOf(mode); + if (index < 0) { + qWarning("Cannot set an unknown QWaylandOutput mode as current"); + return; } - Q_EMIT modeChanged(); + d->currentMode = index; + + Q_EMIT currentModeChanged(); Q_EMIT geometryChanged(); - if (d->window) { - d->window->resize(mode.size); - d->window->setMinimumSize(mode.size); - d->window->setMaximumSize(mode.size); - } + d->sendModesInfo(); } /*! @@ -477,37 +541,12 @@ void QWaylandOutput::setMode(const Mode &mode) * * This property holds the geometry of the QWaylandOutput. * - * \sa QWaylandOutput::mode + * \sa QWaylandOutput::currentMode */ QRect QWaylandOutput::geometry() const { Q_D(const QWaylandOutput); - return QRect(d->position, d->mode.size); -} - -void QWaylandOutput::setGeometry(const QRect &geometry) -{ - Q_D(QWaylandOutput); - if (d->position == geometry.topLeft() && d->mode.size == geometry.size()) - return; - - d->position = geometry.topLeft(); - d->mode.size = geometry.size(); - - Q_FOREACH (QWaylandOutputPrivate::Resource *resource, d->resourceMap().values()) { - d->send_geometry(resource->handle, - d->position.x(), d->position.y(), - d->physicalSize.width(), d->physicalSize.height(), - toWlSubpixel(d->subpixel), d->manufacturer, d->model, - toWlTransform(d->transform)); - d->send_mode(resource->handle, d->mode_current, - d->mode.size.width(), d->mode.size.height(), - d->mode.refreshRate * 1000); - if (resource->version() >= 2) - d->send_done(resource->handle); - } - Q_EMIT positionChanged(); - Q_EMIT modeChanged(); + return QRect(d->position, currentMode().size()); } /*! @@ -527,13 +566,14 @@ void QWaylandOutput::setGeometry(const QRect &geometry) * The available geometry is in output coordinates space, starts from 0,0 and it's as big * as the output by default. * - * \sa QWaylandOutput::mode, QWaylandOutput::geometry + * \sa QWaylandOutput::currentMode, QWaylandOutput::geometry */ QRect QWaylandOutput::availableGeometry() const { Q_D(const QWaylandOutput); + if (!d->availableGeometry.isValid()) - return QRect(QPoint(0, 0), d->mode.size); + return QRect(QPoint(0, 0), currentMode().size()); return d->availableGeometry; } @@ -565,7 +605,7 @@ void QWaylandOutput::setAvailableGeometry(const QRect &availableGeometry) * * This property holds the physical size of the QWaylandOutput in millimeters. * - * \sa QWaylandOutput::geometry, QWaylandOutput::mode + * \sa QWaylandOutput::geometry, QWaylandOutput::currentMode */ QSize QWaylandOutput::physicalSize() const { @@ -755,7 +795,10 @@ void QWaylandOutput::setScaleFactor(int scale) * This property controls whether the size of the WaylandOutput matches the * size of its window. * - * The default is \c true if this WaylandOutput has a window. + * If this property is true, all modes previously added are replaced by a + * mode that matches window size and screen refresh rate. + * + * The default is false. */ /*! @@ -764,7 +807,10 @@ void QWaylandOutput::setScaleFactor(int scale) * This property controls whether the size of the QWaylandOutput matches the * size of its window. * - * The default is \c true if this QWaylandOutput has a window. + * If this property is true, all modes previously added are replaced by a + * mode that matches window size and screen refresh rate. + * + * The default is false. */ bool QWaylandOutput::sizeFollowsWindow() const { @@ -781,13 +827,6 @@ void QWaylandOutput::setSizeFollowsWindow(bool follow) } if (follow != d->sizeFollowsWindow) { - if (follow) { - QObject::connect(d->window, &QWindow::widthChanged, this, &QWaylandOutput::setWidth); - QObject::connect(d->window, &QWindow::heightChanged, this, &QWaylandOutput::setHeight); - } else { - QObject::disconnect(d->window, &QWindow::widthChanged, this, &QWaylandOutput::setWidth); - QObject::disconnect(d->window, &QWindow::heightChanged, this, &QWaylandOutput::setHeight); - } d->sizeFollowsWindow = follow; Q_EMIT sizeFollowsWindowChanged(); } @@ -882,35 +921,63 @@ void QWaylandOutput::surfaceLeave(QWaylandSurface *surface) } /*! - * Sets the width of this QWaylandOutput to \a newWidth. - * - * \sa setHeight, QWaylandOutput::geometry + * \internal */ -void QWaylandOutput::setWidth(int newWidth) +void QWaylandOutput::handleSetWidth(int newWidth) { Q_D(QWaylandOutput); - if (d->mode.size.width() == newWidth) + + if (!d->window || !d->sizeFollowsWindow) return; - QSize s = d->mode.size; - s.setWidth(newWidth); - setGeometry(QRect(d->position, s)); + if (d->currentMode <= d->modes.size() - 1) { + if (d->currentMode >= 0) { + QWaylandOutputMode mode = d->modes.at(d->currentMode); + mode.setWidth(newWidth); + d->modes.replace(d->currentMode, mode); + d->sendModesInfo(); + } else { + // We didn't add a mode during the initialization because the window + // size was invalid, let's add it now + QWaylandOutputMode mode(d->window->size(), + qFloor(d->window->screen()->refreshRate() * 1000)); + if (mode.isValid()) { + d->modes.clear(); + addMode(mode, true); + setCurrentMode(mode); + } + } + } } /*! - * Sets the height of this QWaylandOutput to \a newHeight. - * - * \sa setWidth, QWaylandOutput::geometry + * \internal */ -void QWaylandOutput::setHeight(int newHeight) +void QWaylandOutput::handleSetHeight(int newHeight) { Q_D(QWaylandOutput); - if (d->mode.size.height() == newHeight) + + if (!d->window || !d->sizeFollowsWindow) return; - QSize s = d->mode.size; - s.setHeight(newHeight); - setGeometry(QRect(d->position, s)); + if (d->currentMode <= d->modes.size() - 1) { + if (d->currentMode >= 0) { + QWaylandOutputMode mode = d->modes.at(d->currentMode); + mode.setHeight(newHeight); + d->modes.replace(d->currentMode, mode); + d->sendModesInfo(); + } else { + // We didn't add a mode during the initialization because the window + // size was invalid, let's add it now + QWaylandOutputMode mode(d->window->size(), + qFloor(d->window->screen()->refreshRate() * 1000)); + if (mode.isValid()) { + d->modes.clear(); + addMode(mode, true); + setCurrentMode(mode); + } + } + } } /*! diff --git a/src/compositor/compositor_api/qwaylandoutput.h b/src/compositor/compositor_api/qwaylandoutput.h index e4cbb6109..190231c12 100644 --- a/src/compositor/compositor_api/qwaylandoutput.h +++ b/src/compositor/compositor_api/qwaylandoutput.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2014-2015 Pier Luigi Fiorini +** Copyright (C) 2014-2016 Pier Luigi Fiorini ** Copyright (C) 2013 Klarälvdalens Datakonsult AB (KDAB). ** Contact: http://www.qt.io/licensing/ ** @@ -39,6 +39,7 @@ #define QWAYLANDOUTPUT_H #include +#include #include #include @@ -66,7 +67,6 @@ class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandOutput : public QWaylandObject Q_PROPERTY(QString manufacturer READ manufacturer WRITE setManufacturer NOTIFY manufacturerChanged) Q_PROPERTY(QString model READ model WRITE setModel NOTIFY modelChanged) Q_PROPERTY(QPoint position READ position WRITE setPosition NOTIFY positionChanged) - Q_PROPERTY(QWaylandOutput::Mode mode READ mode WRITE setMode NOTIFY modeChanged) Q_PROPERTY(QRect geometry READ geometry NOTIFY geometryChanged) Q_PROPERTY(QRect availableGeometry READ availableGeometry WRITE setAvailableGeometry NOTIFY availableGeometryChanged) Q_PROPERTY(QSize physicalSize READ physicalSize WRITE setPhysicalSize NOTIFY physicalSizeChanged) @@ -99,12 +99,6 @@ public: }; Q_ENUM(Transform) - struct Mode - { - QSize size; - qreal refreshRate; - }; - QWaylandOutput(); QWaylandOutput(QWaylandCompositor *compositor, QWindow *window); ~QWaylandOutput(); @@ -127,13 +121,14 @@ public: QPoint position() const; void setPosition(const QPoint &pt); - Mode mode() const; - void setMode(const Mode &mode); + QList modes() const; + + void addMode(const QWaylandOutputMode &mode, bool preferred = false); + + QWaylandOutputMode currentMode() const; + void setCurrentMode(const QWaylandOutputMode &mode); QRect geometry() const; - void setGeometry(const QRect &geometry); - void setWidth(int newWidth); - void setHeight(int newHeight); QRect availableGeometry() const; void setAvailableGeometry(const QRect &availableGeometry); @@ -169,7 +164,8 @@ Q_SIGNALS: void windowChanged(); void positionChanged(); void geometryChanged(); - void modeChanged(); + void modeAdded(); + void currentModeChanged(); void availableGeometryChanged(); void physicalSizeChanged(); void scaleFactorChanged(); @@ -182,6 +178,8 @@ Q_SIGNALS: void windowDestroyed(); private Q_SLOTS: + void handleSetWidth(int newWidth); + void handleSetHeight(int newHeight); void handleWindowDestroyed(); protected: @@ -192,6 +190,4 @@ protected: QT_END_NAMESPACE -Q_DECLARE_METATYPE(QWaylandOutput::Mode) - #endif // QWAYLANDOUTPUT_H diff --git a/src/compositor/compositor_api/qwaylandoutput_p.h b/src/compositor/compositor_api/qwaylandoutput_p.h index 9b6ba9076..ea8e7bcd0 100644 --- a/src/compositor/compositor_api/qwaylandoutput_p.h +++ b/src/compositor/compositor_api/qwaylandoutput_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2014-2015 Pier Luigi Fiorini +** Copyright (C) 2014-2016 Pier Luigi Fiorini ** Copyright (C) 2013 Klarälvdalens Datakonsult AB (KDAB). ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ @@ -102,19 +102,25 @@ public: void addView(QWaylandView *view, QWaylandSurface *surface); void removeView(QWaylandView *view, QWaylandSurface *surface); + + void sendGeometry(const Resource *resource); void sendGeometryInfo(); + void sendMode(const Resource *resource, const QWaylandOutputMode &mode); + void sendModesInfo(); + protected: void output_bind_resource(Resource *resource) Q_DECL_OVERRIDE; - private: QWaylandCompositor *compositor; QWindow *window; QString manufacturer; QString model; QPoint position; - QWaylandOutput::Mode mode; + QVector modes; + int currentMode; + int preferredMode; QRect availableGeometry; QVector surfaceViews; QSize physicalSize; diff --git a/src/compositor/compositor_api/qwaylandoutputmode.cpp b/src/compositor/compositor_api/qwaylandoutputmode.cpp new file mode 100644 index 000000000..463b984ad --- /dev/null +++ b/src/compositor/compositor_api/qwaylandoutputmode.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Pier Luigi Fiorini +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandoutputmode.h" +#include "qwaylandoutputmode_p.h" + +/*! + \class QWaylandOutputMode + \inmodule QtWaylandCompositor + \since 5.8 + \brief The QWaylandOutputMode class holds the resolution and refresh rate of an output. + + QWaylandOutputMode holds the resolution and refresh rate of an output. + Resolution is expressed in pixels and refresh rate is measured in mHz. + + \sa QWaylandOutput +*/ + +QWaylandOutputMode::QWaylandOutputMode() + : d(new QWaylandOutputModePrivate) +{ +} + +QWaylandOutputMode::QWaylandOutputMode(const QSize &size, int refreshRate) + : d(new QWaylandOutputModePrivate) +{ + d->size = size; + d->refreshRate = refreshRate; +} + +QWaylandOutputMode::QWaylandOutputMode(const QWaylandOutputMode &other) + : d(new QWaylandOutputModePrivate) +{ + d->size = other.size(); + d->refreshRate = other.refreshRate(); +} + +QWaylandOutputMode::~QWaylandOutputMode() +{ + delete d; +} + +QWaylandOutputMode &QWaylandOutputMode::operator=(const QWaylandOutputMode &other) +{ + d->size = other.size(); + d->refreshRate = other.refreshRate(); + return *this; +} + +/*! + Returns \c true if this mode is equal to \a other, + otherwise returns \c false. +*/ +bool QWaylandOutputMode::operator==(const QWaylandOutputMode &other) const +{ + return size() == other.size() && refreshRate() == refreshRate(); +} + +/*! + Returns \c true if this mode is not equal to \a other, + otherwise returns \c false. +*/ +bool QWaylandOutputMode::operator!=(const QWaylandOutputMode &other) const +{ + return size() != other.size() || refreshRate() != refreshRate(); +} + +/*! + Returns whether this mode contains a valid resolution and refresh rate. +*/ +bool QWaylandOutputMode::isValid() const +{ + return !d->size.isEmpty() && d->refreshRate > 0; +} + +/*! + Returns the resolution in pixels. +*/ +QSize QWaylandOutputMode::size() const +{ + return d->size; +} + +/*! + Returns the refresh rate in mHz. +*/ +int QWaylandOutputMode::refreshRate() const +{ + return d->refreshRate; +} + +/*! + * \internal + */ +void QWaylandOutputMode::setWidth(int width) +{ + d->size.setWidth(width); +} + +/*! + * \internal + */ +void QWaylandOutputMode::setHeight(int height) +{ + d->size.setHeight(height); +} diff --git a/src/compositor/compositor_api/qwaylandoutputmode.h b/src/compositor/compositor_api/qwaylandoutputmode.h new file mode 100644 index 000000000..4ef57f2ee --- /dev/null +++ b/src/compositor/compositor_api/qwaylandoutputmode.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Pier Luigi Fiorini +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDOUTPUTMODE_H +#define QWAYLANDOUTPUTMODE_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandOutputMode +{ +public: + explicit QWaylandOutputMode(); + QWaylandOutputMode(const QSize &size, int refreshRate); + QWaylandOutputMode(const QWaylandOutputMode &other); + ~QWaylandOutputMode(); + + QWaylandOutputMode &operator=(const QWaylandOutputMode &other); + bool operator==(const QWaylandOutputMode &other) const; + bool operator!=(const QWaylandOutputMode &other) const; + + bool isValid() const; + + QSize size() const; + int refreshRate() const; + +private: + class QWaylandOutputModePrivate *const d; + friend class QWaylandOutput; + + void setWidth(int width); + void setHeight(int height); +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDOUTPUTMODE_H diff --git a/src/compositor/compositor_api/qwaylandoutputmode_p.h b/src/compositor/compositor_api/qwaylandoutputmode_p.h new file mode 100644 index 000000000..e9a0eaa37 --- /dev/null +++ b/src/compositor/compositor_api/qwaylandoutputmode_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Pier Luigi Fiorini +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDOUTPUTMODE_P_H +#define QWAYLANDOUTPUTMODE_P_H + +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandOutputModePrivate +{ +public: + QWaylandOutputModePrivate() {} + + QSize size; + int refreshRate = 60000; +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDOUTPUTMODE_P_H diff --git a/tests/auto/compositor/compositor/mockclient.cpp b/tests/auto/compositor/compositor/mockclient.cpp index f2fbc5de2..da1096fb9 100644 --- a/tests/auto/compositor/compositor/mockclient.cpp +++ b/tests/auto/compositor/compositor/mockclient.cpp @@ -52,6 +52,7 @@ MockClient::MockClient() , wlshell(0) , xdgShell(nullptr) , iviApplication(nullptr) + , refreshRate(-1) , error(0 /* means no error according to spec */) , protocolError({0, 0, nullptr}) { @@ -102,10 +103,22 @@ void MockClient::outputGeometryEvent(void *data, wl_output *, resolve(data)->geometry.moveTopLeft(QPoint(x, y)); } -void MockClient::outputModeEvent(void *data, wl_output *, uint32_t, - int w, int h, int) +void MockClient::outputModeEvent(void *data, wl_output *, uint32_t flags, + int w, int h, int refreshRate) { - resolve(data)->geometry.setSize(QSize(w, h)); + QWaylandOutputMode mode(QSize(w, h), refreshRate); + + if (flags & WL_OUTPUT_MODE_CURRENT) { + resolve(data)->geometry.setSize(QSize(w, h)); + resolve(data)->resolution = QSize(w, h); + resolve(data)->refreshRate = refreshRate; + resolve(data)->currentMode = mode; + } + + if (flags & WL_OUTPUT_MODE_PREFERRED) + resolve(data)->preferredMode = mode; + + resolve(data)->modes.append(mode); } void MockClient::outputDone(void *, wl_output *) diff --git a/tests/auto/compositor/compositor/mockclient.h b/tests/auto/compositor/compositor/mockclient.h index ed9319af8..1881393a6 100644 --- a/tests/auto/compositor/compositor/mockclient.h +++ b/tests/auto/compositor/compositor/mockclient.h @@ -34,6 +34,7 @@ #include #include #include +#include class MockSeat; @@ -73,6 +74,11 @@ public: QList m_seats; QRect geometry; + QSize resolution; + int refreshRate; + QWaylandOutputMode currentMode; + QWaylandOutputMode preferredMode; + QList modes; int fd; int error; @@ -106,7 +112,7 @@ private: uint32_t flags, int width, int height, - int refresh); + int refreshRate); static void outputDone(void *data, wl_output *output); static void outputScale(void *data, wl_output *output, int factor); diff --git a/tests/auto/compositor/compositor/tst_compositor.cpp b/tests/auto/compositor/compositor/tst_compositor.cpp index 16aedd8ec..393b9f5b7 100644 --- a/tests/auto/compositor/compositor/tst_compositor.cpp +++ b/tests/auto/compositor/compositor/tst_compositor.cpp @@ -35,6 +35,7 @@ #include "qwaylandbufferref.h" #include "qwaylandseat.h" +#include #include #include #include @@ -58,6 +59,8 @@ private slots: void singleClient(); void multipleClients(); void geometry(); + void modes(); + void sizeFollowsWindow(); void mapSurface(); void frameCallback(); @@ -202,12 +205,61 @@ void tst_WaylandCompositor::geometry() TestCompositor compositor; compositor.create(); - QRect geometry(0, 0, 4096, 3072); - compositor.defaultOutput()->setGeometry(geometry); + QWaylandOutputMode mode(QSize(4096, 3072), 60000); + compositor.defaultOutput()->setPosition(QPoint(1024, 0)); + compositor.defaultOutput()->addMode(mode, true); + compositor.defaultOutput()->setCurrentMode(mode); MockClient client; - QTRY_COMPARE(client.geometry, geometry); + QTRY_COMPARE(client.geometry, QRect(QPoint(1024, 0), QSize(4096, 3072))); + QTRY_COMPARE(client.resolution, QSize(4096, 3072)); + QTRY_COMPARE(client.refreshRate, 60000); +} + +void tst_WaylandCompositor::modes() +{ + TestCompositor compositor; + compositor.create(); + + // mode3 is current, mode4 is preferred + QWaylandOutputMode mode1(QSize(800, 600), 120000); + QWaylandOutputMode mode2(QSize(1024, 768), 100000); + QWaylandOutputMode mode3(QSize(1920, 1080), 60000); + QWaylandOutputMode mode4(QSize(2560, 1440), 59000); + compositor.defaultOutput()->addMode(mode1); + compositor.defaultOutput()->addMode(mode2, true); + compositor.defaultOutput()->addMode(mode3); + compositor.defaultOutput()->addMode(mode4, true); + compositor.defaultOutput()->setCurrentMode(mode3); + + MockClient client; + + QTRY_COMPARE(client.modes.size(), 4); + QTRY_COMPARE(client.currentMode, mode3); + QTRY_COMPARE(client.preferredMode, mode4); + QTRY_COMPARE(client.geometry, QRect(QPoint(0, 0), QSize(1920, 1080))); +} + +void tst_WaylandCompositor::sizeFollowsWindow() +{ + TestCompositor compositor; + + QWindow window; + window.resize(800, 600); + + auto output = new QWaylandOutput(&compositor, &window); + output->setSizeFollowsWindow(true); + + compositor.create(); + + QWaylandOutputMode mode(window.size(), qFloor(window.screen()->refreshRate() * 1000)); + + MockClient client; + + QTRY_COMPARE(client.modes.size(), 1); + QTRY_COMPARE(client.currentMode, mode); + QTRY_COMPARE(client.preferredMode, mode); } void tst_WaylandCompositor::mapSurface() -- cgit v1.2.3