1 | /******************************************************************** |
2 | KWin - the KDE window manager |
3 | This file is part of the KDE project. |
4 | |
5 | Copyright (C) 2011 Arthur Arlt <a.arlt@stud.uni-heidelberg.de> |
6 | Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org> |
7 | |
8 | This program is free software; you can redistribute it and/or modify |
9 | it under the terms of the GNU General Public License as published by |
10 | the Free Software Foundation; either version 2 of the License, or |
11 | (at your option) any later version. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 | *********************************************************************/ |
21 | // own |
22 | #include "outline.h" |
23 | // KWin |
24 | #include "composite.h" |
25 | // KWin libs |
26 | #include <kwinxrenderutils.h> |
27 | #include "workspace.h" |
28 | // Plasma |
29 | #include <Plasma/FrameSvg> |
30 | // Qt |
31 | #include <QPainter> |
32 | // xcb |
33 | #include <xcb/render.h> |
34 | |
35 | namespace KWin { |
36 | |
37 | KWIN_SINGLETON_FACTORY(Outline) |
38 | |
39 | Outline::Outline(QObject *parent) |
40 | : QObject(parent) |
41 | , m_active(false) |
42 | { |
43 | connect(Compositor::self(), SIGNAL(compositingToggled(bool)), SLOT(compositingChanged())); |
44 | } |
45 | |
46 | Outline::~Outline() |
47 | { |
48 | } |
49 | |
50 | void Outline::show() |
51 | { |
52 | m_active = true; |
53 | if (m_visual.isNull()) { |
54 | createHelper(); |
55 | } |
56 | if (m_visual.isNull()) { |
57 | // something went wrong |
58 | return; |
59 | } |
60 | m_visual->show(); |
61 | } |
62 | |
63 | void Outline::hide() |
64 | { |
65 | if (!m_active) { |
66 | return; |
67 | } |
68 | m_active = false; |
69 | if (m_visual.isNull()) { |
70 | return; |
71 | } |
72 | m_visual->hide(); |
73 | } |
74 | |
75 | void Outline::show(const QRect& outlineGeometry) |
76 | { |
77 | setGeometry(outlineGeometry); |
78 | show(); |
79 | } |
80 | |
81 | void Outline::setGeometry(const QRect& outlineGeometry) |
82 | { |
83 | m_outlineGeometry = outlineGeometry; |
84 | } |
85 | |
86 | void Outline::createHelper() |
87 | { |
88 | if (!m_visual.isNull()) { |
89 | return; |
90 | } |
91 | if (Compositor::compositing()) { |
92 | m_visual.reset(new CompositedOutlineVisual(this)); |
93 | } else { |
94 | m_visual.reset(new NonCompositedOutlineVisual(this)); |
95 | } |
96 | } |
97 | |
98 | void Outline::compositingChanged() |
99 | { |
100 | m_visual.reset(); |
101 | if (m_active) { |
102 | show(); |
103 | } |
104 | } |
105 | |
106 | OutlineVisual::OutlineVisual(Outline *outline) |
107 | : m_outline(outline) |
108 | { |
109 | } |
110 | |
111 | OutlineVisual::~OutlineVisual() |
112 | { |
113 | } |
114 | |
115 | CompositedOutlineVisual::CompositedOutlineVisual(Outline *outline) |
116 | : QWidget(NULL, Qt::X11BypassWindowManagerHint) |
117 | , OutlineVisual(outline) |
118 | , m_background(new Plasma::FrameSvg(this)) |
119 | { |
120 | setAttribute(Qt::WA_TranslucentBackground); |
121 | QPalette pal = palette(); |
122 | pal.setColor(backgroundRole(), Qt::transparent); |
123 | setPalette(pal); |
124 | m_background->setImagePath("widgets/translucentbackground" ); |
125 | m_background->setCacheAllRenderedFrames(true); |
126 | m_background->setEnabledBorders(Plasma::FrameSvg::AllBorders); |
127 | } |
128 | |
129 | CompositedOutlineVisual::~CompositedOutlineVisual() |
130 | { |
131 | } |
132 | |
133 | void CompositedOutlineVisual::hide() |
134 | { |
135 | QWidget::hide(); |
136 | } |
137 | |
138 | void CompositedOutlineVisual::show() |
139 | { |
140 | const QRect &outlineGeometry = outline()->geometry(); |
141 | m_background->resizeFrame(outlineGeometry.size()); |
142 | setGeometry(outlineGeometry); |
143 | |
144 | // check which borders to enable |
145 | bool left, right, top, bottom; |
146 | left = right = top = bottom = false; |
147 | const QRect maximizedArea = Workspace::self()->clientArea(MaximizeArea, outlineGeometry.center(), 1); |
148 | if (outlineGeometry.x() == maximizedArea.x()) { |
149 | left = true; |
150 | } |
151 | if (outlineGeometry.y() == maximizedArea.y()) { |
152 | top = true; |
153 | } |
154 | if (outlineGeometry.right() == maximizedArea.right()) { |
155 | right = true; |
156 | } |
157 | if (outlineGeometry.bottom() == maximizedArea.bottom()) { |
158 | bottom = true; |
159 | } |
160 | Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::AllBorders; |
161 | if (left) { |
162 | borders = borders & ~Plasma::FrameSvg::LeftBorder; |
163 | } |
164 | if (right) { |
165 | borders = borders & ~Plasma::FrameSvg::RightBorder; |
166 | } |
167 | if (top) { |
168 | borders = borders & ~Plasma::FrameSvg::TopBorder; |
169 | } |
170 | if (bottom) { |
171 | borders = borders & ~Plasma::FrameSvg::BottomBorder; |
172 | } |
173 | if (left && right && bottom && top) { |
174 | borders = Plasma::FrameSvg::AllBorders; |
175 | } |
176 | m_background->setEnabledBorders(borders); |
177 | QWidget::show(); |
178 | } |
179 | |
180 | void CompositedOutlineVisual::paintEvent(QPaintEvent *) |
181 | { |
182 | QPainter painter(this); |
183 | painter.setRenderHint(QPainter::Antialiasing); |
184 | m_background->paintFrame(&painter); |
185 | } |
186 | |
187 | NonCompositedOutlineVisual::NonCompositedOutlineVisual(Outline *outline) |
188 | : OutlineVisual(outline) |
189 | , m_initialized(false) |
190 | { |
191 | } |
192 | |
193 | NonCompositedOutlineVisual::~NonCompositedOutlineVisual() |
194 | { |
195 | } |
196 | |
197 | void NonCompositedOutlineVisual::show() |
198 | { |
199 | if (!m_initialized) { |
200 | const QRect geo(0, 0, 1, 1); |
201 | const uint32_t values[] = {true}; |
202 | // TODO: use template variant |
203 | m_leftOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); |
204 | m_rightOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); |
205 | m_topOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); |
206 | m_bottomOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); |
207 | m_initialized = true; |
208 | } |
209 | |
210 | const int defaultDepth = Xcb::defaultDepth(); |
211 | |
212 | const QRect &outlineGeometry = outline()->geometry(); |
213 | // left/right parts are between top/bottom, they don't reach as far as the corners |
214 | const uint16_t verticalWidth = 5; |
215 | const uint16_t verticalHeight = outlineGeometry.height() - 10; |
216 | const uint16_t horizontalWidth = outlineGeometry.width(); |
217 | const uint horizontalHeight = 5; |
218 | m_leftOutline.setGeometry(outlineGeometry.x(), outlineGeometry.y() + 5, verticalWidth, verticalHeight); |
219 | m_rightOutline.setGeometry(outlineGeometry.x() + outlineGeometry.width() - 5, outlineGeometry.y() + 5, verticalWidth, verticalHeight); |
220 | m_topOutline.setGeometry(outlineGeometry.x(), outlineGeometry.y(), horizontalWidth, horizontalHeight); |
221 | m_bottomOutline.setGeometry(outlineGeometry.x(), outlineGeometry.y() + outlineGeometry.height() - 5, horizontalWidth, horizontalHeight); |
222 | |
223 | const xcb_render_color_t white = {0xffff, 0xffff, 0xffff, 0xffff}; |
224 | QColor qGray(Qt::gray); |
225 | const xcb_render_color_t gray = { |
226 | uint16_t(0xffff * qGray.redF()), |
227 | uint16_t(0xffff * qGray.greenF()), |
228 | uint16_t(0xffff * qGray.blueF()), |
229 | 0xffff |
230 | }; |
231 | const xcb_render_color_t black = {0, 0, 0, 0xffff}; |
232 | { |
233 | xcb_pixmap_t xpix = xcb_generate_id(connection()); |
234 | xcb_create_pixmap(connection(), defaultDepth, xpix, rootWindow(), verticalWidth, verticalHeight); |
235 | XRenderPicture pic(xpix, defaultDepth); |
236 | |
237 | xcb_rectangle_t rect = {0, 0, 5, verticalHeight}; |
238 | xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, white, 1, &rect); |
239 | rect.x = 1; |
240 | rect.width = 3; |
241 | xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, gray, 1, &rect); |
242 | rect.x = 2; |
243 | rect.width = 1; |
244 | xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, black, 1, &rect); |
245 | |
246 | m_leftOutline.setBackgroundPixmap(xpix); |
247 | m_rightOutline.setBackgroundPixmap(xpix); |
248 | // According to the XSetWindowBackgroundPixmap documentation the pixmap can be freed. |
249 | xcb_free_pixmap(connection(), xpix); |
250 | } |
251 | { |
252 | xcb_pixmap_t xpix = xcb_generate_id(connection()); |
253 | xcb_create_pixmap(connection(), defaultDepth, xpix, rootWindow(), horizontalWidth, horizontalHeight); |
254 | XRenderPicture pic(xpix, defaultDepth); |
255 | |
256 | xcb_rectangle_t rect = {0, 0, horizontalWidth, horizontalHeight}; |
257 | xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, white, 1, &rect); |
258 | xcb_rectangle_t grayRects[] = { |
259 | {1, 1, uint16_t(horizontalWidth -2), 3}, |
260 | {1, 4, 3, 1}, |
261 | {int16_t(horizontalWidth - 4), 4, 3, 1} |
262 | }; |
263 | xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, gray, 3, grayRects); |
264 | xcb_rectangle_t blackRects[] = { |
265 | {2, 2, uint16_t(horizontalWidth -4), 1}, |
266 | {2, 3, 1, 2}, |
267 | {int16_t(horizontalWidth - 3), 3, 1, 2} |
268 | }; |
269 | xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, black, 3, blackRects); |
270 | m_topOutline.setBackgroundPixmap(xpix); |
271 | // According to the XSetWindowBackgroundPixmap documentation the pixmap can be freed. |
272 | xcb_free_pixmap(connection(), xpix); |
273 | } |
274 | { |
275 | xcb_pixmap_t xpix = xcb_generate_id(connection()); |
276 | xcb_create_pixmap(connection(), defaultDepth, xpix, rootWindow(), outlineGeometry.width(), 5); |
277 | XRenderPicture pic(xpix, defaultDepth); |
278 | |
279 | xcb_rectangle_t rect = {0, 0, horizontalWidth, horizontalHeight}; |
280 | xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, white, 1, &rect); |
281 | xcb_rectangle_t grayRects[] = { |
282 | {1, 1, uint16_t(horizontalWidth -2), 3}, |
283 | {1, 0, 3, 1}, |
284 | {int16_t(horizontalWidth - 4), 0, 3, 1} |
285 | }; |
286 | xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, gray, 3, grayRects); |
287 | xcb_rectangle_t blackRects[] = { |
288 | {2, 2, uint16_t(horizontalWidth -4), 1}, |
289 | {2, 0, 1, 2}, |
290 | {int16_t(horizontalWidth - 3), 0, 1, 2} |
291 | }; |
292 | xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, black, 3, blackRects); |
293 | m_bottomOutline.setBackgroundPixmap(xpix); |
294 | // According to the XSetWindowBackgroundPixmap documentation the pixmap can be freed. |
295 | xcb_free_pixmap(connection(), xpix); |
296 | } |
297 | forEachWindow(&Xcb::Window::clear); |
298 | forEachWindow(&Xcb::Window::map); |
299 | } |
300 | |
301 | void NonCompositedOutlineVisual::hide() |
302 | { |
303 | forEachWindow(&Xcb::Window::unmap); |
304 | } |
305 | |
306 | } // namespace |
307 | |