diff options
Diffstat (limited to 'src/FbTk/Shape.cc')
-rw-r--r-- | src/FbTk/Shape.cc | 366 |
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 | |||
47 | using std::min; | ||
48 | |||
49 | namespace FbTk { | ||
50 | |||
51 | namespace { | ||
52 | /* rows is an array of 8 bytes, i.e. 8x8 bits */ | ||
53 | Pixmap 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 | |||
97 | std::vector<Shape::CornerPixmaps> Shape::s_corners; | ||
98 | |||
99 | Shape::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 | |||
113 | Shape::~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 | |||
134 | void 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 | |||
150 | void Shape::setPlaces(int shapeplaces) { | ||
151 | m_shapeplaces = shapeplaces; | ||
152 | } | ||
153 | |||
154 | void 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 | |||
300 | void 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 | */ | ||
320 | void 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 | |||
334 | void Shape::setShapeOffsets(int xoff, int yoff) { | ||
335 | m_shapesource_xoff = xoff; | ||
336 | m_shapesource_yoff = yoff; | ||
337 | update(); | ||
338 | } | ||
339 | |||
340 | void Shape::setShapeNotify(const FbWindow &win) { | ||
341 | #ifdef SHAPE | ||
342 | XShapeSelectInput(App::instance()->display(), | ||
343 | win.window(), ShapeNotifyMask); | ||
344 | #endif // SHAPE | ||
345 | } | ||
346 | |||
347 | bool 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 | ¬_used, ¬_used, // xbs, ybs | ||
357 | ¬_used2, ¬_used2, // wbs, hbs | ||
358 | ¬_used, // cShaped | ||
359 | ¬_used, ¬_used, // xcs, ycs | ||
360 | ¬_used2, ¬_used2); // wcs, hcs | ||
361 | #endif // SHAPE | ||
362 | |||
363 | return (shaped != 0 ? true : false); | ||
364 | } | ||
365 | |||
366 | }; // end namespace FbTk | ||