File indexing completed on 2024-04-28 16:55:13
0001 /*************************************************************************** 0002 * Copyright (C) 2015 Kai Uwe Broulik <kde@privat.broulik.de> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify * 0005 * it under the terms of the GNU General Public License as published by * 0006 * the Free Software Foundation; either version 2 of the License, or * 0007 * (at your option) any later version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, * 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0012 * GNU General Public License for more details. * 0013 * * 0014 * You should have received a copy of the GNU General Public License * 0015 * along with this program; if not, write to the * 0016 * Free Software Foundation, Inc., * 0017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * 0018 ***************************************************************************/ 0019 0020 #include "kwinkscreenhelpereffect.h" 0021 0022 #include <QCoreApplication> 0023 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0024 #include <QX11Info> 0025 #else 0026 #include <private/qtx11extras_p.h> 0027 #endif 0028 #include <chrono> 0029 0030 using namespace std::chrono_literals; 0031 0032 namespace PowerDevil 0033 { 0034 0035 KWinKScreenHelperEffect::KWinKScreenHelperEffect(QObject *parent) : QObject(parent) 0036 { 0037 m_abortTimer.setSingleShot(true); 0038 m_abortTimer.setInterval(11s); 0039 connect(&m_abortTimer, &QTimer::timeout, this, &KWinKScreenHelperEffect::stop); 0040 0041 qApp->installNativeEventFilter(this); 0042 } 0043 0044 KWinKScreenHelperEffect::~KWinKScreenHelperEffect() 0045 { 0046 stop(); 0047 } 0048 0049 bool KWinKScreenHelperEffect::start() 0050 { 0051 m_isValid = checkValid(); 0052 if (!m_isValid) { 0053 // emit fade out right away since the effect is not available 0054 Q_EMIT fadedOut(); 0055 return false; 0056 } 0057 0058 m_running = true; 0059 setEffectProperty(1); 0060 m_abortTimer.start(); 0061 return true; 0062 } 0063 0064 void KWinKScreenHelperEffect::stop() 0065 { 0066 // Maybe somebody got confused, just reset the property then 0067 if (m_state == NormalState) { 0068 setEffectProperty(0); 0069 } else { 0070 setEffectProperty(3); 0071 } 0072 m_running = false; 0073 m_abortTimer.stop(); 0074 } 0075 0076 bool KWinKScreenHelperEffect::checkValid() 0077 { 0078 #if HAVE_XCB 0079 if (QX11Info::isPlatformX11()) { 0080 QScopedPointer<xcb_list_properties_reply_t, QScopedPointerPodDeleter> propsReply(xcb_list_properties_reply(QX11Info::connection(), 0081 xcb_list_properties_unchecked(QX11Info::connection(), QX11Info::appRootWindow()), 0082 nullptr)); 0083 QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atomReply(xcb_intern_atom_reply(QX11Info::connection(), 0084 xcb_intern_atom_unchecked(QX11Info::connection(), false, 25, "_KDE_KWIN_KSCREEN_SUPPORT"), 0085 nullptr)); 0086 0087 if (propsReply.isNull() || atomReply.isNull()) { 0088 return false; 0089 } 0090 0091 auto *atoms = xcb_list_properties_atoms(propsReply.data()); 0092 for (int i = 0; i < propsReply->atoms_len; ++i) { 0093 if (atoms[i] == atomReply->atom) { 0094 m_atom = atomReply->atom; 0095 return true; 0096 } 0097 } 0098 0099 m_atom = 0; 0100 return false; 0101 } 0102 #endif 0103 return false; 0104 } 0105 0106 void KWinKScreenHelperEffect::setEffectProperty(long value) 0107 { 0108 #if HAVE_XCB 0109 if (m_isValid && QX11Info::isPlatformX11()) { 0110 xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, QX11Info::appRootWindow(), m_atom, XCB_ATOM_CARDINAL, 32, 1, &value); 0111 } 0112 #else 0113 Q_UNUSED(value); 0114 #endif 0115 } 0116 0117 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0118 bool KWinKScreenHelperEffect::nativeEventFilter(const QByteArray &eventType, void *message, long *result) 0119 #else 0120 bool KWinKScreenHelperEffect::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) 0121 #endif 0122 { 0123 Q_UNUSED(result); 0124 0125 if (eventType != "xcb_generic_event_t") { 0126 return false; 0127 } 0128 0129 #if HAVE_XCB 0130 if (m_isValid && QX11Info::isPlatformX11()) { 0131 auto e = static_cast<xcb_generic_event_t *>(message); 0132 const uint8_t type = e->response_type & ~0x80; 0133 if (type == XCB_PROPERTY_NOTIFY) { 0134 auto *event = reinterpret_cast<xcb_property_notify_event_t *>(e); 0135 if (event->window == QX11Info::appRootWindow()) { 0136 if (event->atom != m_atom) { 0137 return false; 0138 } 0139 0140 auto cookie = xcb_get_property(QX11Info::connection(), false, QX11Info::appRootWindow(), m_atom, XCB_ATOM_CARDINAL, 0, 1); 0141 QScopedPointer<xcb_get_property_reply_t, QScopedPointerPodDeleter> reply(xcb_get_property_reply(QX11Info::connection(), cookie, nullptr)); 0142 if (reply.isNull() || reply.data()->value_len != 1 || reply.data()->format != uint8_t(32)) { 0143 return false; 0144 } 0145 0146 auto *data = reinterpret_cast<uint32_t *>(xcb_get_property_value(reply.data())); 0147 if (!data) { 0148 return false; 0149 } 0150 0151 switch(*data) { 0152 case 1: 0153 m_state = FadingOutState; 0154 break; 0155 case 2: 0156 m_state = FadedOutState; 0157 if (m_running) { 0158 Q_EMIT fadedOut(); 0159 } 0160 break; 0161 case 3: 0162 m_state = FadingInState; 0163 m_running = false; 0164 m_abortTimer.stop(); 0165 break; 0166 default: 0167 m_state = NormalState; 0168 m_running = false; 0169 } 0170 0171 Q_EMIT stateChanged(m_state); 0172 } 0173 } 0174 } 0175 #else 0176 Q_UNUSED(message); 0177 #endif 0178 0179 return false; 0180 } 0181 0182 } // namespace PowerDevil