aboutsummaryrefslogtreecommitdiff
path: root/src/FbTk/Shape.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/FbTk/Shape.cc')
-rw-r--r--src/FbTk/Shape.cc366
1 files changed, 366 insertions, 0 deletions
diff --git a/src/FbTk/Shape.cc b/src/FbTk/Shape.cc
new file mode 100644
index 0000000..c8cfb46
--- /dev/null
+++ b/src/FbTk/Shape.cc
@@ -0,0 +1,366 @@
1// Shape.cc
2// Copyright (c) 2003 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20// DEALINGS IN THE SOFTWARE.
21
22#include "Shape.hh"
23
24#include "FbWindow.hh"
25#include "App.hh"
26#include "GContext.hh"
27#include "FbPixmap.hh"
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif // HAVE_CONFIG_H
32
33#ifdef HAVE_CSTRING
34 #include <cstring>
35#else
36 #include <string.h>
37#endif
38
39#include <X11/Xutil.h>
40
41#ifdef SHAPE
42#include <X11/extensions/shape.h>
43#endif // SHAPE
44
45#include <algorithm>
46
47using std::min;
48
49namespace FbTk {
50
51namespace {
52/* rows is an array of 8 bytes, i.e. 8x8 bits */
53Pixmap makePixmap(FbWindow &drawable, const unsigned char rows[]) {
54
55 Display *disp = App::instance()->display();
56
57 const size_t data_size = 8 * 8;
58 // we use calloc here so we get consistent C alloc/free with XDestroyImage
59 // and no warnings in valgrind :)
60 char *data = (char *)calloc(data_size, sizeof (char));
61 if (data == 0)
62 return 0;
63
64 memset(data, 0xFF, data_size);
65
66 XImage *ximage = XCreateImage(disp,
67 DefaultVisual(disp, drawable.screenNumber()),
68 1,
69 XYPixmap, 0,
70 data,
71 8, 8,
72 32, 0);
73 if (ximage == 0)
74 return 0;
75
76 XInitImage(ximage);
77
78 for (int y=0; y<8; y++) {
79 for (int x=0; x<8; x++) {
80 XPutPixel(ximage, x, y, (rows[y] & (0x01 << x)) ? 0 : 1); // inverted, it is subtracted
81 }
82 }
83
84 FbPixmap pm(drawable, 8, 8, 1);
85 GContext gc(pm);
86
87 XPutImage(disp, pm.drawable(), gc.gc(), ximage, 0, 0, 0, 0,
88 8, 8);
89
90 XDestroyImage(ximage);
91
92 return pm.release();
93}
94
95};
96
97std::vector<Shape::CornerPixmaps> Shape::s_corners;
98
99Shape::Shape(FbWindow &win, int shapeplaces):
100 m_win(&win),
101 m_shapesource(0),
102 m_shapesource_xoff(0),
103 m_shapesource_yoff(0),
104 m_shapeplaces(shapeplaces) {
105
106#ifdef SHAPE
107 initCorners(win.screenNumber());
108#endif
109
110 update();
111}
112
113Shape::~Shape() {
114
115#ifdef SHAPE
116 if (m_win != 0 && m_win->window()) {
117 // Reset shape of window
118 XShapeCombineMask(App::instance()->display(),
119 m_win->window(),
120 ShapeClip,
121 0, 0,
122 0,
123 ShapeSet);
124 XShapeCombineMask(App::instance()->display(),
125 m_win->window(),
126 ShapeBounding,
127 0, 0,
128 0,
129 ShapeSet);
130 }
131#endif // SHAPE
132}
133
134void Shape::initCorners(int screen_num) {
135 if (s_corners.size() == 0)
136 s_corners.resize(ScreenCount(App::instance()->display()));
137
138 static const unsigned char left_bits[] = { 0xc0, 0xf8, 0xfc, 0xfe, 0xfe, 0xfe, 0xff, 0xff };
139 static const unsigned char right_bits[] = { 0x03, 0x1f, 0x3f, 0x7f, 0x7f, 0x7f, 0xff, 0xff};
140 static const unsigned char bottom_left_bits[] = { 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfc, 0xf8, 0xc0 };
141 static const unsigned char bottom_right_bits[] = { 0xff, 0xff, 0x7f, 0x7f, 0x7f, 0x3f, 0x1f, 0x03 };
142
143 s_corners[screen_num].topleft = makePixmap(*m_win, left_bits);
144 s_corners[screen_num].topright = makePixmap(*m_win, right_bits);
145 s_corners[screen_num].botleft = makePixmap(*m_win, bottom_left_bits);
146 s_corners[screen_num].botright = makePixmap(*m_win, bottom_right_bits);
147
148}
149
150void Shape::setPlaces(int shapeplaces) {
151 m_shapeplaces = shapeplaces;
152}
153
154void Shape::update() {
155 if (m_win == 0 || m_win->window() == 0)
156 return;
157
158#ifdef SHAPE
159 /**
160 * Set the client's shape in position,
161 * or wipe the shape and return.
162 */
163 Display *display = App::instance()->display();
164 int bw = m_win->borderWidth();
165 int width = m_win->width();
166 int height = m_win->height();
167
168 if (m_shapesource == 0 && m_shapeplaces == 0) {
169 /* clear the shape and return */
170 XShapeCombineMask(display,
171 m_win->window(), ShapeClip,
172 0, 0,
173 None, ShapeSet);
174 XShapeCombineMask(display,
175 m_win->window(), ShapeBounding,
176 0, 0,
177 None, ShapeSet);
178 return;
179 }
180
181 XRectangle rect;
182 rect.x = 0;
183 rect.y = 0;
184 rect.width = width;
185 rect.height = height;
186
187 XShapeCombineRectangles(display,
188 m_win->window(), ShapeClip,
189 0, 0, /* offsets */
190 &rect,
191 1, /* number of rectangles */
192 ShapeSet, /* op */
193 2 /* ordering: YXSorted... only 1: doesn't matter */ );
194
195 rect.x = -bw;
196 rect.y = -bw;
197 rect.width = width+2*bw;
198 rect.height = height+2*bw;
199
200 XShapeCombineRectangles(display,
201 m_win->window(), ShapeBounding,
202 0, 0, /* offsets */
203 &rect,
204 1, /* number of rectangles */
205 ShapeSet, /* op */
206 2 /* ordering: YXSorted... only 1: doesn't matter */ );
207
208 if (m_shapesource != 0) {
209
210 /*
211 Copy the shape from the source.
212 We achieve this by subtracting the client-area size from the shape, and then
213 unioning in the client's mask.
214 */
215 rect.x = m_shapesource_xoff;
216 rect.y = m_shapesource_yoff;
217 rect.width = m_shapesource->width();
218 rect.height = m_shapesource->height();
219
220 XShapeCombineRectangles(display,
221 m_win->window(), ShapeClip,
222 0, 0, /* offsets */
223 &rect,
224 1, /* number of rectangles */
225 ShapeSubtract, /* op */
226 2 /* ordering: YXSorted... only 1: doesn't matter */ );
227
228 XShapeCombineShape(display,
229 m_win->window(), ShapeClip,
230 rect.x, rect.y, // xOff, yOff
231 m_shapesource->window(),
232 ShapeClip, ShapeUnion);
233
234 /*
235 Now the bounding rectangle. Note that the frame has a shared border with the region above the
236 client (i.e. titlebar), so we don't want to wipe the shared border, hence the adjustments.
237 */
238 rect.x = m_shapesource_xoff; // note that the full bounding region is already offset by a -borderwidth!
239 rect.y = m_shapesource_yoff;
240 rect.width = m_shapesource->width(); // we don't wipe the outer bounding region [i think]
241 rect.height = m_shapesource->height();
242
243 // we want to delete the client area, dont care about borders really
244 XShapeCombineRectangles(display,
245 m_win->window(), ShapeBounding,
246 0, 0, /* offsets */
247 &rect,
248 1, /* number of rectangles */
249 ShapeSubtract, /* op */
250 2 /* ordering: YXSorted... only 1: doesn't matter */ );
251
252 XShapeCombineShape(display,
253 m_win->window(), ShapeBounding,
254 rect.x , rect.y, // xOff, yOff
255 m_shapesource->window(),
256 ShapeBounding, ShapeUnion);
257 }
258
259
260 CornerPixmaps &corners = s_corners[m_win->screenNumber()];
261#define SHAPECORNER(corner, x, y, shapekind) \
262 XShapeCombineMask(App::instance()->display(), \
263 m_win->window(), \
264 shapekind, \
265 x, y, \
266 corners.corner.drawable(), \
267 ShapeSubtract);
268
269 /**
270 * Set the top corners if the y offset is nonzero.
271 */
272 if (m_shapesource == 0 || m_shapesource_yoff != 0) {
273 if (m_shapeplaces & TOPLEFT) {
274 SHAPECORNER(topleft, 0, 0, ShapeClip);
275 SHAPECORNER(topleft, -bw, -bw, ShapeBounding);
276 }
277 if (m_shapeplaces & TOPRIGHT) {
278 SHAPECORNER(topright, width-8, 0, ShapeClip);
279 SHAPECORNER(topright, width+bw-8, -bw, ShapeBounding);
280 }
281 }
282
283 // note that the bottom corners y-vals are offset by 8 (the height of the corner pixmaps)
284 if (m_shapesource == 0 || (m_shapesource_yoff+(signed) m_shapesource->height()) < height
285 || m_shapesource_yoff >= height /* shaded */) {
286 if (m_shapeplaces & BOTTOMLEFT) {
287 SHAPECORNER(botleft, 0, height-8, ShapeClip);
288 SHAPECORNER(botleft, -bw, height+bw-8, ShapeBounding);
289 }
290 if (m_shapeplaces & BOTTOMRIGHT) {
291 SHAPECORNER(botright, width-8, height-8, ShapeClip);
292 SHAPECORNER(botright, width+bw-8, height+bw-8, ShapeBounding);
293 }
294 }
295
296#endif // SHAPE
297
298}
299
300void Shape::setWindow(FbWindow &win) {
301 m_win = &win;
302 update();
303}
304
305/**
306 * set the shape source to the given window.
307 * This is purely for client windows at the moment, where the offsets and height/width of the
308 * target window and the source window are used to determine whether to shape a given corner.
309 *
310 * (note: xoffset will always be zero, and widths always match, so we ignore those)
311 *
312 * i.e. if the yoffset is not zero, then the top corners are shaped.
313 * if the target height is bigger than the source plus yoffset, then the bottom corners are
314 * shaped.
315 *
316 * If *either* the top or bottom corners are not shaped due to this, but a shape source window
317 * is given, then the bounding shape has the borders alongside the source window deleted, otherwise
318 * they are left hanging outside the client's shape.
319 */
320void Shape::setShapeSource(FbWindow *win, int xoff, int yoff, bool always_update) {
321 if (win != 0 && !isShaped(*win)) {
322 win = 0;
323 if (m_shapesource == 0 && !always_update)
324 return;
325 }
326
327 // even if source is same, want to update the shape on it
328 m_shapesource = win;
329 m_shapesource_xoff = xoff;
330 m_shapesource_yoff = yoff;
331 update();
332}
333
334void Shape::setShapeOffsets(int xoff, int yoff) {
335 m_shapesource_xoff = xoff;
336 m_shapesource_yoff = yoff;
337 update();
338}
339
340void Shape::setShapeNotify(const FbWindow &win) {
341#ifdef SHAPE
342 XShapeSelectInput(App::instance()->display(),
343 win.window(), ShapeNotifyMask);
344#endif // SHAPE
345}
346
347bool Shape::isShaped(const FbWindow &win) {
348 int shaped = 0;
349
350#ifdef SHAPE
351 int not_used;
352 unsigned int not_used2;
353 XShapeQueryExtents(App::instance()->display(),
354 win.window(),
355 &shaped, /// bShaped
356 &not_used, &not_used, // xbs, ybs
357 &not_used2, &not_used2, // wbs, hbs
358 &not_used, // cShaped
359 &not_used, &not_used, // xcs, ycs
360 &not_used2, &not_used2); // wcs, hcs
361#endif // SHAPE
362
363 return (shaped != 0 ? true : false);
364}
365
366}; // end namespace FbTk