diff --git a/abstract_output.h b/abstract_output.h --- a/abstract_output.h +++ b/abstract_output.h @@ -117,6 +117,15 @@ return false; } + /** + * @returns the angle to rotate the display to fit the output + **/ + virtual int softwareRotationAngle() const { + return 0; + } + + QSize orientateSize(const QSize &size) const; + Q_SIGNALS: void modeChanged(); @@ -171,8 +180,6 @@ void setWaylandMode(const QSize &size, int refreshRate); - QSize orientateSize(const QSize &size) const; - private: QPointer m_waylandOutput; QPointer m_xdgOutput; diff --git a/abstract_output.cpp b/abstract_output.cpp --- a/abstract_output.cpp +++ b/abstract_output.cpp @@ -54,7 +54,7 @@ QRect AbstractOutput::geometry() const { - return QRect(m_globalPos, pixelSize() / scale()); + return QRect(m_globalPos, orientateSize(pixelSize()) / scale()); } QSize AbstractOutput::physicalSize() const @@ -101,7 +101,7 @@ m_waylandOutputDevice->setScaleF(scale); } if (m_xdgOutput) { - m_xdgOutput->setLogicalSize(pixelSize() / m_scale); + m_xdgOutput->setLogicalSize(orientateSize(pixelSize()) / m_scale); m_xdgOutput->done(); } emit modeChanged(); @@ -160,7 +160,7 @@ } m_waylandOutput->setCurrentMode(size, refreshRate); if (m_xdgOutput) { - m_xdgOutput->setLogicalSize(pixelSize() / scale()); + m_xdgOutput->setLogicalSize(orientateSize(pixelSize()) / scale()); m_xdgOutput->done(); } } diff --git a/autotests/mock_screens.h b/autotests/mock_screens.h --- a/autotests/mock_screens.h +++ b/autotests/mock_screens.h @@ -37,6 +37,7 @@ float refreshRate(int screen) const override; QSize size(int screen) const override; void init() override; + int rotation(int /*screen*/) const override { return 0; } void setGeometries(const QList &geometries); diff --git a/outputscreens.h b/outputscreens.h --- a/outputscreens.h +++ b/outputscreens.h @@ -43,6 +43,7 @@ QSize size(int screen) const override; qreal scale(int screen) const override; float refreshRate(int screen) const override; + int rotation(int screen) const override; Qt::ScreenOrientation orientation(int screen) const override; void updateCount() override; int number(const QPoint &pos) const override; diff --git a/outputscreens.cpp b/outputscreens.cpp --- a/outputscreens.cpp +++ b/outputscreens.cpp @@ -110,6 +110,14 @@ } return enOuts.at(screen)->orientation(); } +int OutputScreens::rotation(int screen) const +{ + auto output = m_platform->enabledOutputs().value(screen, nullptr); + if (!output) { + return 0; + } + return output->softwareRotationAngle(); +} void OutputScreens::updateCount() { diff --git a/platform.h b/platform.h --- a/platform.h +++ b/platform.h @@ -449,6 +449,8 @@ m_selectedCompositor = type; } + virtual QVector screenRotations() const; + public Q_SLOTS: void pointerMotion(const QPointF &position, quint32 time); void pointerButtonPressed(quint32 button, quint32 time); diff --git a/platform.cpp b/platform.cpp --- a/platform.cpp +++ b/platform.cpp @@ -392,6 +392,11 @@ return QVector({1}); } +QVector Platform::screenRotations() const +{ + return {0}; +} + bool Platform::requiresCompositing() const { return true; diff --git a/platformsupport/scenes/opengl/abstract_egl_backend.h b/platformsupport/scenes/opengl/abstract_egl_backend.h --- a/platformsupport/scenes/opengl/abstract_egl_backend.h +++ b/platformsupport/scenes/opengl/abstract_egl_backend.h @@ -38,6 +38,7 @@ namespace KWin { +class AbstractOutput; class KWIN_EXPORT AbstractEglBackend : public QObject, public OpenGLBackend { @@ -77,6 +78,8 @@ bool createContext(); + static QRect orientateRect(AbstractOutput *output, const QRect &r); + private: void unbindWaylandDisplay(); diff --git a/platformsupport/scenes/opengl/abstract_egl_backend.cpp b/platformsupport/scenes/opengl/abstract_egl_backend.cpp --- a/platformsupport/scenes/opengl/abstract_egl_backend.cpp +++ b/platformsupport/scenes/opengl/abstract_egl_backend.cpp @@ -19,6 +19,7 @@ *********************************************************************/ #include "abstract_egl_backend.h" #include "texture.h" +#include "abstract_output.h" #include "composite.h" #include "egl_context_attribute_builder.h" #include "options.h" @@ -546,5 +547,16 @@ return true; } + +QRect AbstractEglBackend::orientateRect(AbstractOutput *output, const QRect &r) +{ + if (output->softwareRotationAngle() != 0) { + QTransform m; + m.rotate(-output->softwareRotationAngle(), Qt::ZAxis); + return QRect(m.map(r.topLeft()), r.size()); + } + return r; +} + } diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -87,6 +87,10 @@ bool supportsTransformations() const; + int softwareRotationAngle() const override { + return m_softwareRotationAngle; + } + Q_SIGNALS: void dpmsChanged(); @@ -160,6 +164,7 @@ bool m_hasNewCursor = false; bool m_internal = false; bool m_deleted = false; + int m_softwareRotationAngle = 0; }; } diff --git a/plugins/platforms/drm/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp --- a/plugins/platforms/drm/drm_output.cpp +++ b/plugins/platforms/drm/drm_output.cpp @@ -24,6 +24,7 @@ #include "drm_object_connector.h" #include +#include #include "composite.h" #include "logind.h" @@ -713,6 +714,7 @@ using KWayland::Server::OutputDeviceInterface; using KWayland::Server::OutputInterface; auto wlOutput = waylandOutput(); + m_softwareRotationAngle = 0; switch (transform) { case OutputDeviceInterface::Transform::Normal: @@ -804,7 +806,7 @@ QSize DrmOutput::pixelSize() const { - return orientateSize(QSize(m_mode.hdisplay, m_mode.vdisplay)); + return QSize(m_mode.hdisplay, m_mode.vdisplay); } void DrmOutput::setWaylandMode() @@ -897,6 +899,27 @@ } +///Copied from QPlatformScreen +static int angleBetween(Qt::ScreenOrientation a, Qt::ScreenOrientation b) +{ + if (a == Qt::PrimaryOrientation) a = Qt::LandscapeOrientation; + if (b == Qt::PrimaryOrientation) b = Qt::LandscapeOrientation; + + if (a == b) + return 0; + + int ia = std::log2(uint(a)); + int ib = std::log2(uint(b)); + + int delta = ia - ib; + + if (delta < 0) + delta = delta + 4; + + int angles[] = { 0, 90, 180, 270 }; + return angles[delta]; +} + bool DrmOutput::presentAtomically(DrmBuffer *buffer) { if (!LogindIntegration::self()->isActiveSession()) { @@ -919,7 +942,8 @@ // go back to previous state if (m_lastWorkingState.valid) { m_mode = m_lastWorkingState.mode; - setOrientation(m_lastWorkingState.orientation); + m_softwareRotationAngle = angleBetween(m_lastWorkingState.orientation, orientation()); + qCDebug(KWIN_DRM) << "software rotation to" << m_softwareRotationAngle; setGlobalPos(m_lastWorkingState.globalPos); if (m_primaryPlane) { m_primaryPlane->setTransformation(m_lastWorkingState.planeTransformations); diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp --- a/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/plugins/platforms/drm/egl_gbm_backend.cpp @@ -215,9 +215,9 @@ } } -bool EglGbmBackend::makeContextCurrent(const Output &output) +bool EglGbmBackend::makeContextCurrent(const Output &_output) { - const EGLSurface surface = output.eglSurface; + const EGLSurface surface = _output.eglSurface; if (surface == EGL_NO_SURFACE) { return false; } @@ -231,15 +231,15 @@ qCWarning(KWIN_DRM) << "Error occurred while creating context " << error; return false; } - // TODO: ensure the viewport is set correctly each time - const QSize &overall = screens()->size(); - const QRect &v = output.output->geometry(); - // TODO: are the values correct? - - qreal scale = output.output->scale(); + DrmOutput* output = _output.output; + const QRect v = orientateRect(output, output->geometry()); + const qreal scale = output->scale(); + const QSize overall = screens()->size(); + const QSize orientedOverall = output->orientateSize(overall) * scale; glViewport(-v.x() * scale, (v.height() - overall.height() + v.y()) * scale, - overall.width() * scale, overall.height() * scale); + orientedOverall.width(), orientedOverall.height()); + return true; } diff --git a/plugins/platforms/virtual/screens_virtual.h b/plugins/platforms/virtual/screens_virtual.h --- a/plugins/platforms/virtual/screens_virtual.h +++ b/plugins/platforms/virtual/screens_virtual.h @@ -33,6 +33,9 @@ VirtualScreens(VirtualBackend *backend, QObject *parent = nullptr); virtual ~VirtualScreens(); void init() override; + int rotation(int /*screen*/) const override { + return 0; + } private: void createOutputs(); diff --git a/plugins/platforms/virtual/virtual_output.h b/plugins/platforms/virtual/virtual_output.h --- a/plugins/platforms/virtual/virtual_output.h +++ b/plugins/platforms/virtual/virtual_output.h @@ -49,6 +49,10 @@ return m_gammaResult; } + int softwareRotationAngle() const override { + return 0; + } + private: Q_DISABLE_COPY(VirtualOutput); friend class VirtualBackend; diff --git a/plugins/platforms/wayland/egl_wayland_backend.cpp b/plugins/platforms/wayland/egl_wayland_backend.cpp --- a/plugins/platforms/wayland/egl_wayland_backend.cpp +++ b/plugins/platforms/wayland/egl_wayland_backend.cpp @@ -240,13 +240,13 @@ return false; } - const QRect &v = output->m_waylandOutput->geometry(); - - qreal scale = output->m_waylandOutput->scale(); - + const QRect v = orientateRect(output->m_waylandOutput, output->m_waylandOutput->geometry()); + const qreal scale = output->m_waylandOutput->scale(); const QSize overall = screens()->size(); + const QSize orientedOverall = output->m_waylandOutput->orientateSize(overall) * scale; glViewport(-v.x() * scale, (v.height() - overall.height() + v.y()) * scale, - overall.width() * scale, overall.height() * scale); + orientedOverall.width(), orientedOverall.height()); + return true; } diff --git a/plugins/platforms/x11/standalone/screens_xrandr.h b/plugins/platforms/x11/standalone/screens_xrandr.h --- a/plugins/platforms/x11/standalone/screens_xrandr.h +++ b/plugins/platforms/x11/standalone/screens_xrandr.h @@ -41,6 +41,9 @@ float refreshRate(int screen) const override; QSize size(int screen) const override; QSize displaySize() const override; + int rotation(int /*screen*/) const override { + return 0; + } using QObject::event; bool event(xcb_generic_event_t *event) override; diff --git a/plugins/scenes/opengl/scene_opengl.h b/plugins/scenes/opengl/scene_opengl.h --- a/plugins/scenes/opengl/scene_opengl.h +++ b/plugins/scenes/opengl/scene_opengl.h @@ -94,7 +94,7 @@ void handleGraphicsReset(GLenum status); virtual void doPaintBackground(const QVector &vertices) = 0; - virtual void updateProjectionMatrix() = 0; + virtual void updateProjectionMatrix(int rotation) = 0; protected: bool init_ok; @@ -128,12 +128,12 @@ virtual void doPaintBackground(const QVector< float >& vertices); virtual Scene::Window *createWindow(Toplevel *t); virtual void finalDrawWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data); - virtual void updateProjectionMatrix() override; + virtual void updateProjectionMatrix(int rotation) override; void paintCursor() override; private: void performPaintWindow(EffectWindowImpl* w, int mask, QRegion region, WindowPaintData& data); - QMatrix4x4 createProjectionMatrix() const; + QMatrix4x4 createProjectionMatrix(int rotation) const; private: LanczosFilter *m_lanczosFilter; diff --git a/plugins/scenes/opengl/scene_opengl.cpp b/plugins/scenes/opengl/scene_opengl.cpp --- a/plugins/scenes/opengl/scene_opengl.cpp +++ b/plugins/scenes/opengl/scene_opengl.cpp @@ -658,6 +658,7 @@ // trigger start render timer m_backend->prepareRenderingFrame(); for (int i = 0; i < screens()->count(); ++i) { + const int rotation = screens()->rotation(i); const QRect &geo = screens()->geometry(i); QRegion update; QRegion valid; @@ -675,7 +676,8 @@ } int mask = 0; - updateProjectionMatrix(); + updateProjectionMatrix(rotation); + paintScreen(&mask, damage.intersected(geo), repaint, &update, &valid, projectionMatrix(), geo); // call generic implementation paintCursor(); @@ -700,7 +702,7 @@ GLRenderTarget::setVirtualScreenScale(1); int mask = 0; - updateProjectionMatrix(); + updateProjectionMatrix(0); paintScreen(&mask, damage, repaint, &updateRegion, &validRegion, projectionMatrix()); // call generic implementation if (!GLPlatform::instance()->isGLES()) { @@ -962,7 +964,7 @@ } } -QMatrix4x4 SceneOpenGL2::createProjectionMatrix() const +QMatrix4x4 SceneOpenGL2::createProjectionMatrix(int rotation) const { // Create a perspective projection with a 60° field-of-view, // and an aspect ratio of 1.0. @@ -978,6 +980,9 @@ QMatrix4x4 projection; projection.frustum(xMin, xMax, yMin, yMax, zNear, zFar); + if (rotation != 0) { + projection.rotate(rotation, 0, 0, 1); + } // Create a second matrix that transforms screen coordinates // to world coordinates. @@ -994,9 +999,9 @@ return projection * matrix; } -void SceneOpenGL2::updateProjectionMatrix() +void SceneOpenGL2::updateProjectionMatrix(int rotation) { - m_projectionMatrix = createProjectionMatrix(); + m_projectionMatrix = createProjectionMatrix(rotation); } void SceneOpenGL2::paintSimpleScreen(int mask, QRegion region) diff --git a/screens.h b/screens.h --- a/screens.h +++ b/screens.h @@ -157,6 +157,8 @@ return m_orientationSensor; } + virtual int rotation(int screen) const = 0; + public Q_SLOTS: void reconfigure(); @@ -230,11 +232,13 @@ QSize size(int screen) const override; qreal scale(int screen) const override; void updateCount() override; + int rotation(int screen) const override; private: Platform *m_backend; QVector m_geometries; QVector m_scales; + QVector m_rotations; }; inline diff --git a/screens.cpp b/screens.cpp --- a/screens.cpp +++ b/screens.cpp @@ -289,8 +289,14 @@ return 1; } +int BasicScreens::rotation(int screen) const +{ + return m_rotations.value(screen, 0); +} + void BasicScreens::updateCount() { + m_rotations = m_backend->screenRotations(); m_geometries = m_backend->screenGeometries(); m_scales = m_backend->screenScales(); setCount(m_geometries.count());