KWindowSystem

platforms/xcb/kwindoweffects.cpp
1 /*
2  SPDX-FileCopyrightText: 2009 Marco Martin <[email protected]>
3  SPDX-FileCopyrightText: 2014 Martin Gräßlin <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.1-or-later
6 */
7 
8 #include "kwindoweffects_x11.h"
9 
10 #include <QGuiApplication>
11 #include <QVarLengthArray>
12 
13 #include "kwindowsystem.h"
14 #include <config-kwindowsystem.h>
15 
16 #include <QMatrix4x4>
17 #include <QWindow>
18 #include <QX11Info>
19 #include <xcb/xcb.h>
20 
21 using namespace KWindowEffects;
22 
23 KWindowEffectsPrivateX11::KWindowEffectsPrivateX11()
24 {
25 }
26 
27 KWindowEffectsPrivateX11::~KWindowEffectsPrivateX11()
28 {
29 }
30 
31 bool KWindowEffectsPrivateX11::isEffectAvailable(Effect effect)
32 {
33  if (!KWindowSystem::self()->compositingActive()) {
34  return false;
35  }
36  QByteArray effectName;
37 
38  switch (effect) {
39  case Slide:
40  effectName = QByteArrayLiteral("_KDE_SLIDE");
41  break;
42 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82)
43  case PresentWindows:
44  effectName = QByteArrayLiteral("_KDE_PRESENT_WINDOWS_DESKTOP");
45  break;
46  case PresentWindowsGroup:
47  effectName = QByteArrayLiteral("_KDE_PRESENT_WINDOWS_GROUP");
48  break;
49  case HighlightWindows:
50  effectName = QByteArrayLiteral("_KDE_WINDOW_HIGHLIGHT");
51  break;
52 #endif
53  case BlurBehind:
54  effectName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION");
55  break;
56 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 67)
57  case Dashboard:
58  // TODO: Better namespacing for atoms
59  effectName = QByteArrayLiteral("_WM_EFFECT_KDE_DASHBOARD");
60  break;
61 #endif
62  case BackgroundContrast:
63  effectName = QByteArrayLiteral("_KDE_NET_WM_BACKGROUND_CONTRAST_REGION");
64  break;
65  default:
66  return false;
67  }
68 
69  // hackish way to find out if KWin has the effect enabled,
70  // TODO provide proper support
71  xcb_connection_t *c = QX11Info::connection();
72  xcb_list_properties_cookie_t propsCookie = xcb_list_properties_unchecked(c, QX11Info::appRootWindow());
73  xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
74 
75  QScopedPointer<xcb_list_properties_reply_t, QScopedPointerPodDeleter> props(xcb_list_properties_reply(c, propsCookie, nullptr));
76  QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
77  if (!atom || !props) {
78  return false;
79  }
80  xcb_atom_t *atoms = xcb_list_properties_atoms(props.data());
81  for (int i = 0; i < props->atoms_len; ++i) {
82  if (atoms[i] == atom->atom) {
83  return true;
84  }
85  }
86  return false;
87 }
88 
89 void KWindowEffectsPrivateX11::slideWindow(WId id, SlideFromLocation location, int offset)
90 {
91  xcb_connection_t *c = QX11Info::connection();
92  if (!c) {
93  return;
94  }
95 
96  const QByteArray effectName = QByteArrayLiteral("_KDE_SLIDE");
97  xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
98 
99  const int size = 2;
100  int32_t data[size];
101  data[0] = offset;
102 
103  switch (location) {
104  case LeftEdge:
105  data[1] = 0;
106  break;
107  case TopEdge:
108  data[1] = 1;
109  break;
110  case RightEdge:
111  data[1] = 2;
112  break;
113  case BottomEdge:
114  data[1] = 3;
115  default:
116  break;
117  }
118 
119  QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
120  if (!atom) {
121  return;
122  }
123  if (location == NoEdge) {
124  xcb_delete_property(c, id, atom->atom);
125  } else {
126  xcb_change_property(c, XCB_PROP_MODE_REPLACE, id, atom->atom, atom->atom, 32, size, data);
127  }
128 }
129 
130 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 81)
131 QList<QSize> KWindowEffectsPrivateX11::windowSizes(const QList<WId> &ids)
132 {
134  for (WId id : ids) {
135  if (id > 0) {
136  KWindowInfo info(id, NET::WMGeometry | NET::WMFrameExtents);
137  windowSizes.append(info.frameGeometry().size());
138  } else {
139  windowSizes.append(QSize());
140  }
141  }
142  return windowSizes;
143 }
144 #endif
145 
146 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82)
147 void KWindowEffectsPrivateX11::presentWindows(WId controller, const QList<WId> &ids)
148 {
149  xcb_connection_t *c = QX11Info::connection();
150  if (!c) {
151  return;
152  }
153 
154  const int numWindows = ids.count();
155  QVarLengthArray<int32_t, 32> data(numWindows);
156  int actualCount = 0;
157 
158  for (int i = 0; i < numWindows; ++i) {
159  data[i] = ids.at(i);
160  ++actualCount;
161  }
162 
163  if (actualCount != numWindows) {
164  data.resize(actualCount);
165  }
166 
167  if (data.isEmpty()) {
168  return;
169  }
170 
171  const QByteArray effectName = QByteArrayLiteral("_KDE_PRESENT_WINDOWS_GROUP");
172  xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
173  QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
174  if (!atom) {
175  return;
176  }
177  xcb_change_property(c, XCB_PROP_MODE_REPLACE, controller, atom->atom, atom->atom, 32, data.size(), data.constData());
178 }
179 #endif
180 
181 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82)
182 void KWindowEffectsPrivateX11::presentWindows(WId controller, int desktop)
183 {
184  xcb_connection_t *c = QX11Info::connection();
185  if (!c) {
186  return;
187  }
188  const QByteArray effectName = QByteArrayLiteral("_KDE_PRESENT_WINDOWS_DESKTOP");
189  xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
190  QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
191  if (!atom) {
192  return;
193  }
194 
195  int32_t data = desktop;
196  xcb_change_property(c, XCB_PROP_MODE_REPLACE, controller, atom->atom, atom->atom, 32, 1, &data);
197 }
198 #endif
199 
200 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82)
201 void KWindowEffectsPrivateX11::highlightWindows(WId controller, const QList<WId> &ids)
202 {
203  xcb_connection_t *c = QX11Info::connection();
204  if (!c) {
205  return;
206  }
207  const QByteArray effectName = QByteArrayLiteral("_KDE_WINDOW_HIGHLIGHT");
208  xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
209  QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
210  if (!atom) {
211  return;
212  }
213 
214  const int numWindows = ids.count();
215  if (numWindows == 0) {
216  xcb_delete_property(c, controller, atom->atom);
217  return;
218  }
219 
220  QVarLengthArray<int32_t, 32> data(numWindows);
221  int actualCount = 0;
222 
223  for (int i = 0; i < numWindows; ++i) {
224  data[i] = ids.at(i);
225  ++actualCount;
226  }
227 
228  if (actualCount != numWindows) {
229  data.resize(actualCount);
230  }
231 
232  if (data.isEmpty()) {
233  return;
234  }
235  xcb_change_property(c, XCB_PROP_MODE_REPLACE, controller, atom->atom, atom->atom, 32, data.size(), data.constData());
236 }
237 #endif
238 
239 void KWindowEffectsPrivateX11::enableBlurBehind(WId window, bool enable, const QRegion &region)
240 {
241  xcb_connection_t *c = QX11Info::connection();
242  if (!c) {
243  return;
244  }
245  const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION");
246  xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
247  QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
248  if (!atom) {
249  return;
250  }
251 
252  if (enable) {
253  QVector<uint32_t> data;
254  data.reserve(region.rectCount() * 4);
255  for (const QRect &r : region) {
256  // kwin on X uses device pixels, convert from logical
257  auto dpr = qApp->devicePixelRatio();
258  data << r.x() * dpr << r.y() * dpr << r.width() * dpr << r.height() * dpr;
259  }
260 
261  xcb_change_property(c, XCB_PROP_MODE_REPLACE, window, atom->atom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData());
262  } else {
263  xcb_delete_property(c, window, atom->atom);
264  }
265 }
266 
267 void KWindowEffectsPrivateX11::setBackgroundFrost(QWindow *window, QColor color, const QRegion &region)
268 {
269  auto id = window->winId();
270 
271  xcb_connection_t *c = QX11Info::connection();
272  const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_BACKGROUND_FROST_REGION");
273  xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
274  QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
275  if (!atom) {
276  return;
277  }
278 
279  if (!color.isValid()) {
280  xcb_delete_property(c, id, atom->atom);
281  return;
282  }
283 
284  enableBackgroundContrast(id, false);
285 
286  QVector<uint32_t> data;
287  data.reserve(region.rectCount() * 4 + 4);
288  for (const QRect &r : region) {
289  auto dpr = qApp->devicePixelRatio();
290  data << r.x() * dpr << r.y() * dpr << r.width() * dpr << r.height() * dpr;
291  }
292 
293  data << color.red();
294  data << color.green();
295  data << color.blue();
296  data << color.alpha();
297 
298  xcb_change_property(c, XCB_PROP_MODE_REPLACE, id, atom->atom, atom->atom, 32, data.size(), data.constData());
299 }
300 
301 void KWindowEffectsPrivateX11::enableBackgroundContrast(WId window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion &region)
302 {
303  xcb_connection_t *c = QX11Info::connection();
304  const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_BACKGROUND_CONTRAST_REGION");
305  xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
306  QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
307  if (!atom) {
308  return;
309  }
310 
311  if (enable) {
313  QVector<uint32_t> data;
314  data.reserve(region.rectCount() * 4 + 16);
315  for (const QRect &r : region) {
316  auto dpr = qApp->devicePixelRatio();
317  data << r.x() * dpr << r.y() * dpr << r.width() * dpr << r.height() * dpr;
318  }
319 
320  QMatrix4x4 satMatrix; // saturation
321  QMatrix4x4 intMatrix; // intensity
322  QMatrix4x4 contMatrix; // contrast
323 
324  // clang-format off
325 
326  //Saturation matrix
327  if (!qFuzzyCompare(saturation, 1.0)) {
328  const qreal rval = (1.0 - saturation) * .2126;
329  const qreal gval = (1.0 - saturation) * .7152;
330  const qreal bval = (1.0 - saturation) * .0722;
331 
332  satMatrix = QMatrix4x4(rval + saturation, rval, rval, 0.0,
333  gval, gval + saturation, gval, 0.0,
334  bval, bval, bval + saturation, 0.0,
335  0, 0, 0, 1.0);
336  }
337 
338  //IntensityMatrix
339  if (!qFuzzyCompare(intensity, 1.0)) {
340  intMatrix.scale(intensity, intensity, intensity);
341  }
342 
343  //Contrast Matrix
344  if (!qFuzzyCompare(contrast, 1.0)) {
345  const float transl = (1.0 - contrast) / 2.0;
346 
347  contMatrix = QMatrix4x4(contrast, 0, 0, 0.0,
348  0, contrast, 0, 0.0,
349  0, 0, contrast, 0.0,
350  transl, transl, transl, 1.0);
351  }
352 
353  // clang-format on
354 
355  QMatrix4x4 colorMatrix = contMatrix * satMatrix * intMatrix;
356  colorMatrix = colorMatrix.transposed();
357 
358  uint32_t *rawData = reinterpret_cast<uint32_t *>(colorMatrix.data());
359 
360  for (int i = 0; i < 16; ++i) {
361  data << rawData[i];
362  }
363 
364  xcb_change_property(c, XCB_PROP_MODE_REPLACE, window, atom->atom, atom->atom, 32, data.size(), data.constData());
365  } else {
366  xcb_delete_property(c, window, atom->atom);
367  }
368 }
369 
370 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 67)
371 void KWindowEffectsPrivateX11::markAsDashboard(WId window)
372 {
373  static const char DASHBOARD_WIN_CLASS[] = "dashboard\0dashboard";
374  xcb_connection_t *c = QX11Info::connection();
375  if (!c) {
376  return;
377  }
378  xcb_change_property(c, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8, 19, DASHBOARD_WIN_CLASS);
379 }
380 #endif
void scale(const QVector3D &vector)
LeftEdge
QList< QSize > windowSizes(const QList< WId > &ids)
const T & at(int i) const const
This class provides information about a given window in the platform specific windowing system...
Definition: kwindowinfo.h:62
int length() const const
void setBackgroundFrost(QWindow *window, QColor frostColor, const QRegion &region)
Instructs the window manager to modify the color of the background in the specified region behind the...
int count(const T &value) const const
void append(const T &value)
int red() const const
const char * constData() const const
const T * constData() const const
int alpha() const const
int green() const const
void reserve(int size)
static KWindowSystem * self()
Access to the singleton instance.
float * data()
int blue() const const
Namespace for common standardized window effects.
QMatrix4x4 transposed() const const
int size() const const
QWindow * fromWinId(WId id)
WId winId() const const
void enableBackgroundContrast(WId window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion &region)
Instructs the window manager to modify the color of the background in the specified region behind the...
bool isValid() const const
int rectCount() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Fri Oct 15 2021 22:41:50 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.