1 | /* |
2 | * Copyright 2009 Marco Martin <notmart@gmail.com> |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU Library General Public License as |
6 | * published by the Free Software Foundation; either version 2, or |
7 | * (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details |
13 | * |
14 | * You should have received a copy of the GNU Library General Public |
15 | * License along with this program; if not, write to the |
16 | * Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | */ |
19 | |
20 | #include "windoweffects.h" |
21 | #include <QVarLengthArray> |
22 | |
23 | #include <kwindowsystem.h> |
24 | |
25 | #include "theme.h" |
26 | |
27 | #ifdef Q_WS_X11 |
28 | #include <X11/Xlib.h> |
29 | #include <X11/Xatom.h> |
30 | #include <X11/Xutil.h> |
31 | #include <QX11Info> |
32 | |
33 | #define DASHBOARD_WIN_NAME "dashboard" |
34 | #define DASHBOARD_WIN_CLASS "dashboard" |
35 | #endif |
36 | |
37 | namespace Plasma |
38 | { |
39 | |
40 | namespace WindowEffects |
41 | { |
42 | |
43 | //FIXME: check if this works for any atom? |
44 | bool isEffectAvailable(Effect effect) |
45 | { |
46 | if (!Plasma::Theme::defaultTheme()->windowTranslucencyEnabled()) { |
47 | return false; |
48 | } |
49 | #ifdef Q_WS_X11 |
50 | QString effectName; |
51 | |
52 | switch (effect) { |
53 | case Slide: |
54 | effectName = "_KDE_SLIDE" ; |
55 | break; |
56 | case WindowPreview: |
57 | effectName = "_KDE_WINDOW_PREVIEW" ; |
58 | break; |
59 | case PresentWindows: |
60 | effectName = "_KDE_PRESENT_WINDOWS_DESKTOP" ; |
61 | break; |
62 | case PresentWindowsGroup: |
63 | effectName = "_KDE_PRESENT_WINDOWS_GROUP" ; |
64 | break; |
65 | case HighlightWindows: |
66 | effectName = "_KDE_WINDOW_HIGHLIGHT" ; |
67 | break; |
68 | case OverrideShadow: |
69 | effectName = "_KDE_SHADOW_OVERRIDE" ; |
70 | break; |
71 | case BlurBehind: |
72 | effectName = "_KDE_NET_WM_BLUR_BEHIND_REGION" ; |
73 | break; |
74 | case Dashboard: |
75 | // TODO: Better namespacing for atoms |
76 | effectName = "_WM_EFFECT_KDE_DASHBOARD" ; |
77 | break; |
78 | default: |
79 | return false; |
80 | } |
81 | |
82 | // hackish way to find out if KWin has the effect enabled, |
83 | // TODO provide proper support |
84 | Display *dpy = QX11Info::display(); |
85 | Atom atom = XInternAtom(dpy, effectName.toLatin1(), False); |
86 | int cnt; |
87 | Atom *list = XListProperties(dpy, DefaultRootWindow(dpy), &cnt); |
88 | if (list != NULL) { |
89 | bool ret = (qFind(list, list + cnt, atom) != list + cnt); |
90 | XFree(list); |
91 | return ret; |
92 | } |
93 | #endif |
94 | return false; |
95 | } |
96 | |
97 | void slideWindow(WId id, Plasma::Location location, int offset) |
98 | { |
99 | #ifdef Q_WS_X11 |
100 | Display *dpy = QX11Info::display(); |
101 | Atom atom = XInternAtom( dpy, "_KDE_SLIDE" , False ); |
102 | QVarLengthArray<long, 2> data(2); |
103 | |
104 | data[0] = offset; |
105 | |
106 | switch (location) { |
107 | case LeftEdge: |
108 | data[1] = 0; |
109 | break; |
110 | case TopEdge: |
111 | data[1] = 1; |
112 | break; |
113 | case RightEdge: |
114 | data[1] = 2; |
115 | break; |
116 | case BottomEdge: |
117 | data[1] = 3; |
118 | default: |
119 | break; |
120 | } |
121 | |
122 | if (location == Desktop || location == Floating) { |
123 | XDeleteProperty(dpy, id, atom); |
124 | } else { |
125 | XChangeProperty(dpy, id, atom, atom, 32, PropModeReplace, |
126 | reinterpret_cast<unsigned char *>(data.data()), data.size()); |
127 | } |
128 | #endif |
129 | } |
130 | |
131 | void slideWindow(QWidget *widget, Plasma::Location location) |
132 | { |
133 | #ifdef Q_WS_X11 |
134 | Display *dpy = QX11Info::display(); |
135 | Atom atom = XInternAtom( dpy, "_KDE_SLIDE" , False ); |
136 | QVarLengthArray<long, 2> data(2); |
137 | data[0] = -1; |
138 | |
139 | switch (location) { |
140 | case LeftEdge: |
141 | data[1] = 0; |
142 | break; |
143 | case TopEdge: |
144 | data[1] = 1; |
145 | break; |
146 | case RightEdge: |
147 | data[1] = 2; |
148 | break; |
149 | case BottomEdge: |
150 | data[1] = 3; |
151 | default: |
152 | break; |
153 | } |
154 | |
155 | if (location == Desktop || location == Floating) { |
156 | XDeleteProperty(dpy, widget->effectiveWinId(), atom); |
157 | } else { |
158 | XChangeProperty(dpy, widget->effectiveWinId(), atom, atom, 32, PropModeReplace, |
159 | reinterpret_cast<unsigned char *>(data.data()), data.size()); |
160 | } |
161 | #endif |
162 | } |
163 | |
164 | QList<QSize> windowSizes(const QList<WId> &ids) |
165 | { |
166 | QList<QSize> windowSizes; |
167 | foreach (WId id, ids) { |
168 | #ifdef Q_WS_X11 |
169 | if (id > 0) { |
170 | KWindowInfo info = KWindowSystem::windowInfo(id, NET::WMGeometry|NET::WMFrameExtents); |
171 | windowSizes.append(info.frameGeometry().size()); |
172 | } else { |
173 | windowSizes.append(QSize()); |
174 | } |
175 | #else |
176 | windowSizes.append(QSize()); |
177 | #endif |
178 | } |
179 | return windowSizes; |
180 | } |
181 | |
182 | void showWindowThumbnails(WId parent, const QList<WId> &windows, const QList<QRect> &rects) |
183 | { |
184 | if (windows.size() != rects.size()) { |
185 | return; |
186 | } |
187 | #ifdef Q_WS_X11 |
188 | Display *dpy = QX11Info::display(); |
189 | Atom atom = XInternAtom(dpy, "_KDE_WINDOW_PREVIEW" , False); |
190 | if (windows.isEmpty()) { |
191 | XDeleteProperty(dpy, parent, atom); |
192 | return; |
193 | } |
194 | |
195 | int numWindows = windows.size(); |
196 | |
197 | // 64 is enough for 10 windows and is a nice base 2 number |
198 | QVarLengthArray<long, 64> data(1 + (6 * numWindows)); |
199 | data[0] = numWindows; |
200 | |
201 | QList<WId>::const_iterator windowsIt; |
202 | QList<QRect>::const_iterator rectsIt = rects.constBegin(); |
203 | int i = 0; |
204 | for (windowsIt = windows.constBegin(); windowsIt != windows.constEnd(); ++windowsIt) { |
205 | |
206 | const int start = (i * 6) + 1; |
207 | const QRect thumbnailRect = (*rectsIt); |
208 | |
209 | data[start] = 5; |
210 | data[start+1] = (*windowsIt); |
211 | data[start+2] = thumbnailRect.x(); |
212 | data[start+3] = thumbnailRect.y(); |
213 | data[start+4] = thumbnailRect.width(); |
214 | data[start+5] = thumbnailRect.height(); |
215 | ++rectsIt; |
216 | ++i; |
217 | } |
218 | |
219 | XChangeProperty(dpy, parent, atom, atom, 32, PropModeReplace, |
220 | reinterpret_cast<unsigned char *>(data.data()), data.size()); |
221 | #endif |
222 | } |
223 | |
224 | void presentWindows(WId controller, const QList<WId> &ids) |
225 | { |
226 | #ifdef Q_WS_X11 |
227 | const int numWindows = ids.count(); |
228 | QVarLengthArray<long, 32> data(numWindows); |
229 | int actualCount = 0; |
230 | |
231 | for (int i = 0; i < numWindows; ++i) { |
232 | data[i] = ids.at(i); |
233 | ++actualCount; |
234 | |
235 | } |
236 | |
237 | if (actualCount != numWindows) { |
238 | data.resize(actualCount); |
239 | } |
240 | |
241 | if (!data.isEmpty()) { |
242 | Display *dpy = QX11Info::display(); |
243 | Atom atom = XInternAtom(dpy, "_KDE_PRESENT_WINDOWS_GROUP" , False); |
244 | XChangeProperty(dpy, controller, atom, atom, 32, PropModeReplace, |
245 | reinterpret_cast<unsigned char *>(data.data()), data.size()); |
246 | } |
247 | #endif |
248 | } |
249 | |
250 | void presentWindows(WId controller, int desktop) |
251 | { |
252 | #ifdef Q_WS_X11 |
253 | QVarLengthArray<long, 1> data(1); |
254 | data[0] = desktop; |
255 | Display *dpy = QX11Info::display(); |
256 | Atom atom = XInternAtom(dpy, "_KDE_PRESENT_WINDOWS_DESKTOP" , False); |
257 | XChangeProperty(dpy, controller, atom, atom, 32, PropModeReplace, |
258 | reinterpret_cast<unsigned char *>(data.data()), data.size()); |
259 | #endif |
260 | } |
261 | |
262 | void highlightWindows(WId controller, const QList<WId> &ids) |
263 | { |
264 | #ifdef Q_WS_X11 |
265 | const int numWindows = ids.count(); |
266 | Display *dpy = QX11Info::display(); |
267 | Atom atom = XInternAtom(dpy, "_KDE_WINDOW_HIGHLIGHT" , False); |
268 | |
269 | if (numWindows == 0) { |
270 | Atom atom = XInternAtom(dpy, "_KDE_WINDOW_HIGHLIGHT" , False); |
271 | XDeleteProperty(dpy, controller, atom); |
272 | } |
273 | |
274 | QVarLengthArray<long, 32> data(numWindows); |
275 | int actualCount = 0; |
276 | |
277 | for (int i = 0; i < numWindows; ++i) { |
278 | data[i] = ids.at(i); |
279 | ++actualCount; |
280 | |
281 | } |
282 | |
283 | if (actualCount != numWindows) { |
284 | data.resize(actualCount); |
285 | } |
286 | |
287 | if (!data.isEmpty()) { |
288 | XChangeProperty(dpy, controller, atom, atom, 32, PropModeReplace, |
289 | reinterpret_cast<unsigned char *>(data.data()), data.size()); |
290 | } |
291 | #endif |
292 | } |
293 | |
294 | void overrideShadow(WId window, bool override) |
295 | { |
296 | #ifdef Q_WS_X11 |
297 | Display *dpy = QX11Info::display(); |
298 | Atom atom = XInternAtom( dpy, "_KDE_SHADOW_OVERRIDE" , False ); |
299 | if (!override) { |
300 | XDeleteProperty(dpy, window, atom); |
301 | } else { |
302 | QVarLengthArray<long, 1> data(1); |
303 | data[0] = 1; |
304 | XChangeProperty(dpy, window, atom, atom, 32, PropModeReplace, |
305 | reinterpret_cast<unsigned char *>(data.data()), data.size()); |
306 | } |
307 | #endif |
308 | } |
309 | |
310 | void enableBlurBehind(WId window, bool enable, const QRegion ®ion) |
311 | { |
312 | #ifdef Q_WS_X11 |
313 | Display *dpy = QX11Info::display(); |
314 | Atom atom = XInternAtom(dpy, "_KDE_NET_WM_BLUR_BEHIND_REGION" , False); |
315 | |
316 | if (enable) { |
317 | QVector<QRect> rects = region.rects(); |
318 | QVector<unsigned long> data; |
319 | foreach (const QRect &r, rects) { |
320 | data << r.x() << r.y() << r.width() << r.height(); |
321 | } |
322 | |
323 | XChangeProperty(dpy, window, atom, XA_CARDINAL, 32, PropModeReplace, |
324 | reinterpret_cast<const unsigned char *>(data.constData()), data.size()); |
325 | } else { |
326 | XDeleteProperty(dpy, window, atom); |
327 | } |
328 | #endif |
329 | } |
330 | |
331 | void markAsDashboard(WId window) |
332 | { |
333 | #ifdef Q_WS_X11 |
334 | XClassHint classHint; |
335 | classHint.res_name = DASHBOARD_WIN_NAME; |
336 | classHint.res_class = DASHBOARD_WIN_CLASS; |
337 | XSetClassHint(QX11Info::display(), window, &classHint); |
338 | #endif |
339 | } |
340 | |
341 | } |
342 | |
343 | } |
344 | |