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"