File indexing completed on 2024-04-28 03:59:24
0001 /* 0002 SPDX-FileCopyrightText: 2009 Marco Martin <notmart@gmail.com> 0003 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #include "kwindoweffects_x11.h" 0009 0010 #include <QGuiApplication> 0011 #include <QVarLengthArray> 0012 0013 #include "kx11extras.h" 0014 #include <config-kwindowsystem.h> 0015 0016 #include <QMatrix4x4> 0017 #include <QWindow> 0018 #include <private/qtx11extras_p.h> 0019 0020 #include <xcb/xcb.h> 0021 0022 #include "cptr_p.h" 0023 #include <cmath> 0024 0025 using namespace KWindowEffects; 0026 0027 KWindowEffectsPrivateX11::KWindowEffectsPrivateX11() 0028 { 0029 } 0030 0031 KWindowEffectsPrivateX11::~KWindowEffectsPrivateX11() 0032 { 0033 } 0034 0035 bool KWindowEffectsPrivateX11::isEffectAvailable(Effect effect) 0036 { 0037 if (!KX11Extras::self()->compositingActive()) { 0038 return false; 0039 } 0040 QByteArray effectName; 0041 0042 switch (effect) { 0043 case Slide: 0044 effectName = QByteArrayLiteral("_KDE_SLIDE"); 0045 break; 0046 case BlurBehind: 0047 effectName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION"); 0048 break; 0049 case BackgroundContrast: 0050 effectName = QByteArrayLiteral("_KDE_NET_WM_BACKGROUND_CONTRAST_REGION"); 0051 break; 0052 default: 0053 return false; 0054 } 0055 0056 // hackish way to find out if KWin has the effect enabled, 0057 // TODO provide proper support 0058 xcb_connection_t *c = QX11Info::connection(); 0059 xcb_list_properties_cookie_t propsCookie = xcb_list_properties_unchecked(c, QX11Info::appRootWindow()); 0060 xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData()); 0061 0062 UniqueCPointer<xcb_list_properties_reply_t> props(xcb_list_properties_reply(c, propsCookie, nullptr)); 0063 UniqueCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, atomCookie, nullptr)); 0064 if (!atom || !props) { 0065 return false; 0066 } 0067 xcb_atom_t *atoms = xcb_list_properties_atoms(props.get()); 0068 for (int i = 0; i < props->atoms_len; ++i) { 0069 if (atoms[i] == atom->atom) { 0070 return true; 0071 } 0072 } 0073 return false; 0074 } 0075 0076 void KWindowEffectsPrivateX11::slideWindow(QWindow *window, SlideFromLocation location, int offset) 0077 { 0078 xcb_connection_t *c = QX11Info::connection(); 0079 if (!c) { 0080 return; 0081 } 0082 0083 const QByteArray effectName = QByteArrayLiteral("_KDE_SLIDE"); 0084 xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData()); 0085 0086 const int size = 2; 0087 int32_t data[size]; 0088 data[0] = offset; 0089 0090 switch (location) { 0091 case LeftEdge: 0092 data[1] = 0; 0093 break; 0094 case TopEdge: 0095 data[1] = 1; 0096 break; 0097 case RightEdge: 0098 data[1] = 2; 0099 break; 0100 case BottomEdge: 0101 data[1] = 3; 0102 default: 0103 break; 0104 } 0105 0106 UniqueCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, atomCookie, nullptr)); 0107 if (!atom) { 0108 return; 0109 } 0110 if (location == NoEdge) { 0111 xcb_delete_property(c, window->winId(), atom->atom); 0112 } else { 0113 xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, atom->atom, 32, size, data); 0114 } 0115 } 0116 0117 void KWindowEffectsPrivateX11::enableBlurBehind(QWindow *window, bool enable, const QRegion ®ion) 0118 { 0119 xcb_connection_t *c = QX11Info::connection(); 0120 if (!c) { 0121 return; 0122 } 0123 const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION"); 0124 xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData()); 0125 UniqueCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, atomCookie, nullptr)); 0126 if (!atom) { 0127 return; 0128 } 0129 0130 if (enable) { 0131 QList<uint32_t> data; 0132 data.reserve(region.rectCount() * 4); 0133 for (const QRect &r : region) { 0134 // kwin on X uses device pixels, convert from logical 0135 auto dpr = qApp->devicePixelRatio(); 0136 data << std::floor(r.x() * dpr) << std::floor(r.y() * dpr) << std::ceil(r.width() * dpr) << std::ceil(r.height() * dpr); 0137 } 0138 0139 xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData()); 0140 } else { 0141 xcb_delete_property(c, window->winId(), atom->atom); 0142 } 0143 } 0144 0145 void KWindowEffectsPrivateX11::enableBackgroundContrast(QWindow *window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion ®ion) 0146 { 0147 xcb_connection_t *c = QX11Info::connection(); 0148 const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_BACKGROUND_CONTRAST_REGION"); 0149 xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData()); 0150 UniqueCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, atomCookie, nullptr)); 0151 if (!atom) { 0152 return; 0153 } 0154 0155 if (enable) { 0156 QList<uint32_t> data; 0157 data.reserve(region.rectCount() * 4 + 16); 0158 for (const QRect &r : region) { 0159 auto dpr = qApp->devicePixelRatio(); 0160 data << std::floor(r.x() * dpr) << std::floor(r.y() * dpr) << std::ceil(r.width() * dpr) << std::ceil(r.height() * dpr); 0161 } 0162 0163 QMatrix4x4 satMatrix; // saturation 0164 QMatrix4x4 intMatrix; // intensity 0165 QMatrix4x4 contMatrix; // contrast 0166 0167 // clang-format off 0168 0169 //Saturation matrix 0170 if (!qFuzzyCompare(saturation, 1.0)) { 0171 const qreal rval = (1.0 - saturation) * .2126; 0172 const qreal gval = (1.0 - saturation) * .7152; 0173 const qreal bval = (1.0 - saturation) * .0722; 0174 0175 satMatrix = QMatrix4x4(rval + saturation, rval, rval, 0.0, 0176 gval, gval + saturation, gval, 0.0, 0177 bval, bval, bval + saturation, 0.0, 0178 0, 0, 0, 1.0); 0179 } 0180 0181 //IntensityMatrix 0182 if (!qFuzzyCompare(intensity, 1.0)) { 0183 intMatrix.scale(intensity, intensity, intensity); 0184 } 0185 0186 //Contrast Matrix 0187 if (!qFuzzyCompare(contrast, 1.0)) { 0188 const float transl = (1.0 - contrast) / 2.0; 0189 0190 contMatrix = QMatrix4x4(contrast, 0, 0, 0.0, 0191 0, contrast, 0, 0.0, 0192 0, 0, contrast, 0.0, 0193 transl, transl, transl, 1.0); 0194 } 0195 0196 // clang-format on 0197 0198 QMatrix4x4 colorMatrix = contMatrix * satMatrix * intMatrix; 0199 colorMatrix = colorMatrix.transposed(); 0200 0201 uint32_t *rawData = reinterpret_cast<uint32_t *>(colorMatrix.data()); 0202 0203 for (int i = 0; i < 16; ++i) { 0204 data << rawData[i]; 0205 } 0206 0207 xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, atom->atom, 32, data.size(), data.constData()); 0208 } else { 0209 xcb_delete_property(c, window->winId(), atom->atom); 0210 } 0211 }