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 @@ -25,6 +25,8 @@ // Qt #include +typedef uint32_t xcb_randr_crtc_t; + namespace KWin { @@ -41,21 +43,38 @@ float refreshRate(int screen) const override; QSize size(int screen) const override; QSize displaySize() const override; + bool isInternal(int screen) const override; + bool supportsTransformations(int screen) const override; using QObject::event; bool event(xcb_generic_event_t *event) override; + enum class Transformation { + Rotate0 = 1 << 0, + Rotate90 = 1 << 1, + Rotate180 = 1 << 2, + Rotate270 = 1 << 3, + ReflectX = 1 << 4, + ReflectY = 1 << 5 + }; + Q_DECLARE_FLAGS(Transformations, Transformation); + protected Q_SLOTS: void updateCount() override; private: template void update(); + void automaticRotation(); QVector m_geometries; QVector m_refreshRates; QVector m_names; + QVector m_internals; + QVector m_supportedTransformations; + xcb_randr_crtc_t m_internalCrtc = 0; }; } // namespace +Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::XRandRScreens::Transformations); #endif diff --git a/plugins/platforms/x11/standalone/screens_xrandr.cpp b/plugins/platforms/x11/standalone/screens_xrandr.cpp --- a/plugins/platforms/x11/standalone/screens_xrandr.cpp +++ b/plugins/platforms/x11/standalone/screens_xrandr.cpp @@ -23,6 +23,7 @@ #include "options.h" #include "workspace.h" #endif +#include "orientation_sensor.h" #include "xcbutils.h" @@ -33,6 +34,7 @@ : Screens(parent) , X11EventFilter(Xcb::Extensions::self()->randrNotifyEvent()) { + connect(orientationSensor(), &OrientationSensor::orientationChanged, this, &XRandRScreens::automaticRotation); } XRandRScreens::~XRandRScreens() = default; @@ -48,6 +50,8 @@ }; m_geometries.clear(); m_names.clear(); + m_internals.clear(); + m_supportedTransformations.clear(); if (!Xcb::Extensions::self()->isRandrAvailable()) { fallback(); return; @@ -106,6 +110,22 @@ } } m_names << name; + const bool internal = (name.startsWith(QLatin1String("LVDS")) || name.startsWith(QLatin1String("eDP"))); + m_internals << internal; + if (internal) { + m_internalCrtc = crtcs[i]; + } + + Transformations transformations = Transformation::Rotate0; + auto testTransformation = [&transformations, &info] (xcb_randr_rotation_t t, Transformation our) { + if (info->rotations & t) { + transformations |= our; + } + }; + testTransformation(XCB_RANDR_ROTATION_ROTATE_90, Transformation::Rotate90); + testTransformation(XCB_RANDR_ROTATION_ROTATE_90, Transformation::Rotate180); + testTransformation(XCB_RANDR_ROTATION_ROTATE_90, Transformation::Rotate270); + m_supportedTransformations << transformations; } } if (m_geometries.isEmpty()) { @@ -227,4 +247,72 @@ return QSize(screen->width_in_pixels, screen->height_in_pixels); } +bool XRandRScreens::isInternal(int screen) const +{ + if (screen >= m_internals.size()) { + return false; + } + return m_internals.at(screen); +} + +bool XRandRScreens::supportsTransformations(int screen) const +{ + if (screen >= m_supportedTransformations.size()) { + return false; + } + const auto transformations = m_supportedTransformations.at(screen); + return transformations.testFlag(Transformation::Rotate90) + || transformations.testFlag(Transformation::Rotate180) + || transformations.testFlag(Transformation::Rotate270); +} + +void XRandRScreens::automaticRotation() +{ + const int index = m_internals.indexOf(true); + if (index == -1) { + return; + } + if (index >= m_supportedTransformations.size()) { + return; + } + const auto supportedTransformations = m_supportedTransformations.at(index); + const auto requestedTransformation = orientationSensor()->orientation(); + xcb_randr_rotation_t newTransformation = XCB_RANDR_ROTATION_ROTATE_0; + switch (requestedTransformation) { + case OrientationSensor::Orientation::TopUp: + newTransformation = XCB_RANDR_ROTATION_ROTATE_0; + break; + case OrientationSensor::Orientation::TopDown: + if (!supportedTransformations.testFlag(Transformation::Rotate180)) { + return; + } + newTransformation = XCB_RANDR_ROTATION_ROTATE_180; + break; + case OrientationSensor::Orientation::LeftUp: + if (!supportedTransformations.testFlag(Transformation::Rotate90)) { + return; + } + newTransformation = XCB_RANDR_ROTATION_ROTATE_90; + break; + case OrientationSensor::Orientation::RightUp: + if (!supportedTransformations.testFlag(Transformation::Rotate270)) { + return; + } + newTransformation = XCB_RANDR_ROTATION_ROTATE_270; + break; + case OrientationSensor::Orientation::FaceUp: + case OrientationSensor::Orientation::FaceDown: + case OrientationSensor::Orientation::Undefined: + // unsupported + return; + } + Xcb::RandR::ScreenResources resources{rootWindow()}; + Xcb::RandR::CrtcInfo info(m_internalCrtc, resources->config_timestamp); + if (!info) { + return; + } + + Xcb::RandR::SetCrtcConfig(m_internalCrtc, resources->timestamp, resources->config_timestamp, info->x, info->y, info->mode, newTransformation, info->num_outputs, info.outputs()); +} + } // namespace