File indexing completed on 2024-04-28 05:36:15

0001 /*
0002  *   SPDX-FileCopyrightText: 2015 Kai Uwe Broulik <kde@privat.broulik.de>
0003  *
0004  *   SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kwinkscreenhelpereffect.h"
0008 
0009 #include <QCoreApplication>
0010 #include <chrono>
0011 #include <private/qtx11extras_p.h>
0012 
0013 using namespace std::chrono_literals;
0014 
0015 namespace PowerDevil
0016 {
0017 KWinKScreenHelperEffect::KWinKScreenHelperEffect(QObject *parent)
0018     : QObject(parent)
0019 {
0020     m_abortTimer.setSingleShot(true);
0021     m_abortTimer.setInterval(11s);
0022     connect(&m_abortTimer, &QTimer::timeout, this, &KWinKScreenHelperEffect::stop);
0023 
0024     qApp->installNativeEventFilter(this);
0025 }
0026 
0027 KWinKScreenHelperEffect::~KWinKScreenHelperEffect()
0028 {
0029     stop();
0030 }
0031 
0032 bool KWinKScreenHelperEffect::start()
0033 {
0034     m_isValid = checkValid();
0035     if (!m_isValid) {
0036         // emit fade out right away since the effect is not available
0037         Q_EMIT fadedOut();
0038         return false;
0039     }
0040 
0041     m_running = true;
0042     setEffectProperty(1);
0043     m_abortTimer.start();
0044     return true;
0045 }
0046 
0047 void KWinKScreenHelperEffect::stop()
0048 {
0049     // Maybe somebody got confused, just reset the property then
0050     if (m_state == NormalState) {
0051         setEffectProperty(0);
0052     } else {
0053         setEffectProperty(3);
0054     }
0055     m_running = false;
0056     m_abortTimer.stop();
0057 }
0058 
0059 bool KWinKScreenHelperEffect::checkValid()
0060 {
0061 #if HAVE_XCB
0062     if (QX11Info::isPlatformX11()) {
0063         QScopedPointer<xcb_list_properties_reply_t, QScopedPointerPodDeleter> propsReply(
0064             xcb_list_properties_reply(QX11Info::connection(), xcb_list_properties_unchecked(QX11Info::connection(), QX11Info::appRootWindow()), nullptr));
0065         QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atomReply(
0066             xcb_intern_atom_reply(QX11Info::connection(), xcb_intern_atom_unchecked(QX11Info::connection(), false, 25, "_KDE_KWIN_KSCREEN_SUPPORT"), nullptr));
0067 
0068         if (propsReply.isNull() || atomReply.isNull()) {
0069             return false;
0070         }
0071 
0072         auto *atoms = xcb_list_properties_atoms(propsReply.data());
0073         for (int i = 0; i < propsReply->atoms_len; ++i) {
0074             if (atoms[i] == atomReply->atom) {
0075                 m_atom = atomReply->atom;
0076                 return true;
0077             }
0078         }
0079 
0080         m_atom = 0;
0081         return false;
0082     }
0083 #endif
0084     return false;
0085 }
0086 
0087 void KWinKScreenHelperEffect::setEffectProperty(long value)
0088 {
0089 #if HAVE_XCB
0090     if (m_isValid && QX11Info::isPlatformX11()) {
0091         xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, QX11Info::appRootWindow(), m_atom, XCB_ATOM_CARDINAL, 32, 1, &value);
0092     }
0093 #else
0094     Q_UNUSED(value);
0095 #endif
0096 }
0097 
0098 bool KWinKScreenHelperEffect::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
0099 {
0100     Q_UNUSED(result);
0101 
0102     if (eventType != "xcb_generic_event_t") {
0103         return false;
0104     }
0105 
0106 #if HAVE_XCB
0107     if (m_isValid && QX11Info::isPlatformX11()) {
0108         auto e = static_cast<xcb_generic_event_t *>(message);
0109         const uint8_t type = e->response_type & ~0x80;
0110         if (type == XCB_PROPERTY_NOTIFY) {
0111             auto *event = reinterpret_cast<xcb_property_notify_event_t *>(e);
0112             if (event->window == QX11Info::appRootWindow()) {
0113                 if (event->atom != m_atom) {
0114                     return false;
0115                 }
0116 
0117                 auto cookie = xcb_get_property(QX11Info::connection(), false, QX11Info::appRootWindow(), m_atom, XCB_ATOM_CARDINAL, 0, 1);
0118                 QScopedPointer<xcb_get_property_reply_t, QScopedPointerPodDeleter> reply(xcb_get_property_reply(QX11Info::connection(), cookie, nullptr));
0119                 if (reply.isNull() || reply.data()->value_len != 1 || reply.data()->format != uint8_t(32)) {
0120                     return false;
0121                 }
0122 
0123                 auto *data = reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.data()));
0124                 if (!data) {
0125                     return false;
0126                 }
0127 
0128                 switch (*data) {
0129                 case 1:
0130                     m_state = FadingOutState;
0131                     break;
0132                 case 2:
0133                     m_state = FadedOutState;
0134                     if (m_running) {
0135                         Q_EMIT fadedOut();
0136                     }
0137                     break;
0138                 case 3:
0139                     m_state = FadingInState;
0140                     m_running = false;
0141                     m_abortTimer.stop();
0142                     break;
0143                 default:
0144                     m_state = NormalState;
0145                     m_running = false;
0146                 }
0147 
0148                 Q_EMIT stateChanged(m_state);
0149             }
0150         }
0151     }
0152 #else
0153     Q_UNUSED(message);
0154 #endif
0155 
0156     return false;
0157 }
0158 
0159 } // namespace PowerDevil
0160 
0161 #include "moc_kwinkscreenhelpereffect.cpp"