Warning, file /plasma/libkscreen/src/libdpms/waylanddpmshelper.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 // SPDX-FileCopyrightText: 2015 by Martin Gräßlin <mgraesslin@kde.org>
0002 // SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org>
0003 //
0004 // SPDX-License-Identifier: LGPL-2.1-or-later
0005 
0006 #include "kscreendpms_debug.h"
0007 #include "waylanddpmshelper_p.h"
0008 
0009 #include "qwayland-dpms.h"
0010 #include <QDebug>
0011 #include <QGuiApplication>
0012 #include <QPointer>
0013 #include <QScreen>
0014 #include <QVector>
0015 #include <QWaylandClientExtensionTemplate>
0016 #include <qpa/qplatformnativeinterface.h>
0017 
0018 class Dpms : public QObject, public QtWayland::org_kde_kwin_dpms
0019 {
0020 public:
0021     Dpms(struct ::org_kde_kwin_dpms *object, WaylandDpmsHelper *dpms, QScreen *parent)
0022         : QObject(parent)
0023         , org_kde_kwin_dpms(object)
0024         , m_screen(parent)
0025         , m_dpms(dpms)
0026     {
0027     }
0028 
0029     ~Dpms()
0030     {
0031         release();
0032     }
0033 
0034     bool isSupported() const
0035     {
0036         return m_supported;
0037     }
0038 
0039     void org_kde_kwin_dpms_supported(uint32_t supported) override
0040     {
0041         m_pendingSupported = supported;
0042     }
0043 
0044     void org_kde_kwin_dpms_mode(uint32_t newMode) override
0045     {
0046         m_mode = mode(newMode);
0047     }
0048 
0049     void org_kde_kwin_dpms_done() override
0050     {
0051         m_supported = m_pendingSupported;
0052         KScreen::Dpms::Mode mode;
0053         switch (m_mode) {
0054         case Dpms::mode_On:
0055             mode = KScreen::Dpms::On;
0056             break;
0057         case Dpms::mode_Standby:
0058             mode = KScreen::Dpms::Standby;
0059             break;
0060         case Dpms::mode_Suspend:
0061             mode = KScreen::Dpms::Suspend;
0062             break;
0063         case Dpms::mode_Off:
0064             mode = KScreen::Dpms::Off;
0065             break;
0066         }
0067         if (m_dpms) {
0068             Q_EMIT m_dpms->modeChanged(mode, m_screen);
0069         }
0070     }
0071 
0072     QScreen *const m_screen;
0073     QPointer<WaylandDpmsHelper> m_dpms;
0074     bool m_supported = false;
0075     bool m_pendingSupported = false;
0076     mode m_mode;
0077 };
0078 
0079 class DpmsManager : public QWaylandClientExtensionTemplate<::DpmsManager>, public QtWayland::org_kde_kwin_dpms_manager
0080 {
0081 public:
0082     DpmsManager(WaylandDpmsHelper *dpms)
0083         : QWaylandClientExtensionTemplate<DpmsManager>(1)
0084         , m_dpms(dpms)
0085     {
0086         connect(this, &DpmsManager::activeChanged, this, [this] {
0087             const bool hasDpms = isActive();
0088             if (hasDpms) {
0089                 qCDebug(KSCREEN_DPMS) << "Compositor provides a DpmsManager";
0090             } else {
0091                 qCDebug(KSCREEN_DPMS) << "Compositor does not provide a DpmsManager";
0092                 m_dpms->setSupported(hasDpms);
0093                 return;
0094             }
0095 
0096             const auto screens = qGuiApp->screens();
0097             for (QScreen *screen : screens) {
0098                 addScreen(screen);
0099             }
0100             connect(qGuiApp, &QGuiApplication::screenAdded, this, &DpmsManager::addScreen);
0101             connect(qGuiApp, &QGuiApplication::screenRemoved, this, [this](QScreen *screen) {
0102                 delete m_dpmsPerScreen.take(screen);
0103             });
0104             m_dpms->setSupported(hasDpms);
0105         });
0106     }
0107     ~DpmsManager()
0108     {
0109         qDeleteAll(m_dpmsPerScreen);
0110     }
0111 
0112     Dpms *fetch(QScreen *screen)
0113     {
0114         return m_dpmsPerScreen.value(screen);
0115     }
0116 
0117 private:
0118     void addScreen(QScreen *screen)
0119     {
0120         // We can't rely on checking the wl_output being null yet
0121         // https://codereview.qt-project.org/c/qt/qtwayland/+/464669
0122         const bool fake = screen->geometry().isEmpty() || screen->name().isEmpty();
0123         if (fake) {
0124             return;
0125         }
0126 
0127         QPlatformNativeInterface *native = qGuiApp->platformNativeInterface();
0128         wl_output *output = reinterpret_cast<wl_output *>(native->nativeResourceForScreen(QByteArrayLiteral("output"), screen));
0129         if (output) {
0130             m_dpmsPerScreen[screen] = new Dpms(get(output), m_dpms, screen);
0131         }
0132     }
0133 
0134     WaylandDpmsHelper *const m_dpms;
0135     QHash<QScreen *, Dpms *> m_dpmsPerScreen;
0136 };
0137 
0138 WaylandDpmsHelper::WaylandDpmsHelper()
0139     : AbstractDpmsHelper()
0140     , m_dpmsManager(new DpmsManager(this))
0141 {
0142 }
0143 
0144 WaylandDpmsHelper::~WaylandDpmsHelper()
0145 {
0146     delete m_dpmsManager;
0147 }
0148 
0149 void WaylandDpmsHelper::trigger(KScreen::Dpms::Mode mode, const QList<QScreen *> &screens)
0150 {
0151     Q_ASSERT(isSupported());
0152 
0153     if (screens.isEmpty()) {
0154         return;
0155     }
0156 
0157     setHasPendingChanges(true);
0158 
0159     auto level = Dpms::mode_On;
0160     switch (mode) {
0161     case KScreen::Dpms::Toggle: {
0162         for (QScreen *screen : screens) {
0163             auto dpms = m_dpmsManager->fetch(screen);
0164             if (!dpms || !dpms->isSupported()) {
0165                 qCDebug(KSCREEN_DPMS) << "screen does not provide dpms" << screen;
0166                 continue;
0167             }
0168             if (dpms->m_mode == Dpms::mode_On) {
0169                 dpms->set(Dpms::mode_Off);
0170             } else {
0171                 dpms->set(Dpms::mode_On);
0172             }
0173         }
0174     }
0175         return;
0176     case KScreen::Dpms::Off:
0177         level = Dpms::mode_Off;
0178         break;
0179     case KScreen::Dpms::Standby:
0180         level = Dpms::mode_Standby;
0181         break;
0182     case KScreen::Dpms::Suspend:
0183         level = Dpms::mode_Suspend;
0184         break;
0185     case KScreen::Dpms::On:
0186         level = Dpms::mode_On;
0187         break;
0188     }
0189     for (auto screen : screens) {
0190         auto dpms = m_dpmsManager->fetch(screen);
0191         if (dpms) {
0192             dpms->set(level);
0193         }
0194     }
0195     setHasPendingChanges(false);
0196 }
0197 
0198 void WaylandDpmsHelper::blockUntilSupported()
0199 {
0200     QMetaObject::invokeMethod(m_dpmsManager, "addRegistryListener");
0201     if (!m_dpmsManager->isActive()) {
0202         setSupported(false);
0203     }
0204 }