diff options
author | simonb <simonb> | 2007-08-09 03:45:31 (GMT) |
---|---|---|
committer | simonb <simonb> | 2007-08-09 03:45:31 (GMT) |
commit | a0f44b9e9a7c2e401e2cf1ef80fed98acf1d7e44 (patch) | |
tree | 2c2658372e5e85c2d0c186f7080e9534430af633 /src/Shape.cc | |
parent | a04eed16c5287c11e565ecb25f465b96d6f61279 (diff) | |
download | fluxbox-a0f44b9e9a7c2e401e2cf1ef80fed98acf1d7e44.zip fluxbox-a0f44b9e9a7c2e401e2cf1ef80fed98acf1d7e44.tar.bz2 |
Fix handling of Shape, stage 2 (more involved/complete handling)
Diffstat (limited to 'src/Shape.cc')
-rw-r--r-- | src/Shape.cc | 334 |
1 files changed, 217 insertions, 117 deletions
diff --git a/src/Shape.cc b/src/Shape.cc index a073b9f..7198d00 100644 --- a/src/Shape.cc +++ b/src/Shape.cc | |||
@@ -48,25 +48,13 @@ | |||
48 | 48 | ||
49 | using std::min; | 49 | using std::min; |
50 | 50 | ||
51 | // ignore_border basically means do clip shape instead of bounding shape | 51 | namespace { |
52 | FbTk::FbPixmap *Shape::createShape(bool ignore_border) { | 52 | /* rows is an array of 8 bytes, i.e. 8x8 bits */ |
53 | if (m_win->window() == 0 || m_shapeplaces == 0 || | 53 | Pixmap makePixmap(FbTk::FbWindow &drawable, const unsigned char rows[]) { |
54 | m_win->width() < 3 || m_win->height() < 3) | ||
55 | return 0; | ||
56 | |||
57 | static char left_bits[] = { 0xc0, 0xf8, 0xfc, 0xfe, 0xfe, 0xfe, 0xff, 0xff }; | ||
58 | static char right_bits[] = { 0x03, 0x1f, 0x3f, 0x7f, 0x7f, 0x7f, 0xff, 0xff}; | ||
59 | static char bottom_left_bits[] = { 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfc, 0xf8, 0xc0 }; | ||
60 | static char bottom_right_bits[] = { 0xff, 0xff, 0x7f, 0x7f, 0x7f, 0x3f, 0x1f, 0x03 }; | ||
61 | |||
62 | const int borderw = m_win->borderWidth(); | ||
63 | const int win_width = m_win->width() + (ignore_border?0:2*borderw); | ||
64 | const int win_height = m_win->height() + (ignore_border?0:2*borderw); | ||
65 | const int pixmap_width = min(8, win_width); | ||
66 | const int pixmap_height = min(8, win_height); | ||
67 | 54 | ||
68 | Display *disp = FbTk::App::instance()->display(); | 55 | Display *disp = FbTk::App::instance()->display(); |
69 | const size_t data_size = win_width * win_height; | 56 | |
57 | const size_t data_size = 8 * 8; | ||
70 | // we use calloc here so we get consistent C alloc/free with XDestroyImage | 58 | // we use calloc here so we get consistent C alloc/free with XDestroyImage |
71 | // and no warnings in valgrind :) | 59 | // and no warnings in valgrind :) |
72 | char *data = (char *)calloc(data_size, sizeof (char)); | 60 | char *data = (char *)calloc(data_size, sizeof (char)); |
@@ -76,74 +64,50 @@ FbTk::FbPixmap *Shape::createShape(bool ignore_border) { | |||
76 | memset(data, 0xFF, data_size); | 64 | memset(data, 0xFF, data_size); |
77 | 65 | ||
78 | XImage *ximage = XCreateImage(disp, | 66 | XImage *ximage = XCreateImage(disp, |
79 | DefaultVisual(disp, m_win->screenNumber()), | 67 | DefaultVisual(disp, drawable.screenNumber()), |
80 | 1, | 68 | 1, |
81 | XYPixmap, 0, | 69 | XYPixmap, 0, |
82 | data, | 70 | data, |
83 | win_width, win_height, | 71 | 8, 8, |
84 | 32, 0); | 72 | 32, 0); |
85 | if (ximage == 0) | 73 | if (ximage == 0) |
86 | return 0; | 74 | return 0; |
87 | 75 | ||
88 | XInitImage(ximage); | 76 | XInitImage(ximage); |
89 | 77 | ||
90 | // shape corners | 78 | for (int y=0; y<8; y++) { |
91 | 79 | for (int x=0; x<8; x++) { | |
92 | if (m_shapeplaces & Shape::TOPLEFT) { | 80 | XPutPixel(ximage, x, y, (rows[y] & (0x01 << x)) ? 0 : 1); // inverted, it is subtracted |
93 | for (int y=0; y<pixmap_height; y++) { | ||
94 | for (int x=0; x<pixmap_width; x++) { | ||
95 | XPutPixel(ximage, x, y, (left_bits[y] & (0x01 << x)) ? 1 : 0); | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | |||
100 | if (m_shapeplaces & Shape::TOPRIGHT) { | ||
101 | for (int y=0; y<pixmap_height; y++) { | ||
102 | for (int x=0; x<pixmap_width; x++) { | ||
103 | XPutPixel(ximage, x + win_width - pixmap_width, y, | ||
104 | (right_bits[y] & (0x01 << x)) ? 1 : 0); | ||
105 | } | ||
106 | } | 81 | } |
107 | } | 82 | } |
108 | 83 | ||
109 | if (m_shapeplaces & Shape::BOTTOMLEFT) { | 84 | FbTk::FbPixmap pm(drawable, 8, 8, 1); |
110 | for (int y=0; y<pixmap_height; y++) { | 85 | FbTk::GContext gc(pm); |
111 | for (int x=0; x<pixmap_width; x++) { | ||
112 | XPutPixel(ximage, x, y + win_height - pixmap_height, | ||
113 | (bottom_left_bits[y] & (0x01 << x)) ? 1 : 0); | ||
114 | } | ||
115 | } | ||
116 | } | ||
117 | |||
118 | if (m_shapeplaces & Shape::BOTTOMRIGHT) { | ||
119 | for (int y=0; y<pixmap_height; y++) { | ||
120 | for (int x=0; x<pixmap_width; x++) { | ||
121 | XPutPixel(ximage, x + win_width - pixmap_width, y + win_height - pixmap_height, | ||
122 | (bottom_right_bits[y] & (0x01 << x)) ? 1 : 0); | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
127 | FbTk::FbPixmap *pm = new FbTk::FbPixmap(*m_win, win_width, win_height, 1); | ||
128 | |||
129 | |||
130 | FbTk::GContext gc(*pm); | ||
131 | 86 | ||
132 | XPutImage(disp, pm->drawable(), gc.gc(), ximage, 0, 0, 0, 0, | 87 | XPutImage(disp, pm.drawable(), gc.gc(), ximage, 0, 0, 0, 0, |
133 | win_width, win_height); | 88 | 8, 8); |
134 | 89 | ||
135 | XDestroyImage(ximage); | 90 | XDestroyImage(ximage); |
136 | 91 | ||
137 | return pm; | 92 | return pm.release(); |
138 | |||
139 | } | 93 | } |
140 | 94 | ||
95 | }; | ||
96 | |||
97 | std::vector<Shape::CornerPixmaps> Shape::s_corners; | ||
98 | |||
141 | Shape::Shape(FbTk::FbWindow &win, int shapeplaces): | 99 | Shape::Shape(FbTk::FbWindow &win, int shapeplaces): |
142 | m_win(&win), | 100 | m_win(&win), |
101 | m_shapesource(0), | ||
102 | m_shapesource_xoff(0), | ||
103 | m_shapesource_yoff(0), | ||
143 | m_shapeplaces(shapeplaces) { | 104 | m_shapeplaces(shapeplaces) { |
144 | 105 | ||
145 | m_clipshape.reset(createShape(true)); | 106 | #ifdef SHAPE |
146 | m_boundingshape.reset(createShape(false)); | 107 | initCorners(win.screenNumber()); |
108 | #endif | ||
109 | |||
110 | update(); | ||
147 | } | 111 | } |
148 | 112 | ||
149 | Shape::~Shape() { | 113 | Shape::~Shape() { |
@@ -157,19 +121,32 @@ Shape::~Shape() { | |||
157 | 0, 0, | 121 | 0, 0, |
158 | 0, | 122 | 0, |
159 | ShapeSet); | 123 | ShapeSet); |
160 | // Reset shape of window | 124 | XShapeCombineMask(FbTk::App::instance()->display(), |
161 | if (m_boundingshape.get()) { | 125 | m_win->window(), |
162 | XShapeCombineMask(FbTk::App::instance()->display(), | 126 | ShapeBounding, |
163 | m_win->window(), | 127 | 0, 0, |
164 | ShapeBounding, | 128 | 0, |
165 | 0, 0, | 129 | ShapeSet); |
166 | 0, | 130 | } |
167 | ShapeSet); | ||
168 | } | ||
169 | } | ||
170 | #endif // SHAPE | 131 | #endif // SHAPE |
171 | } | 132 | } |
172 | 133 | ||
134 | void Shape::initCorners(int screen_num) { | ||
135 | if (s_corners.size() == 0) | ||
136 | s_corners.resize(ScreenCount(FbTk::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 | |||
173 | void Shape::setPlaces(int shapeplaces) { | 150 | void Shape::setPlaces(int shapeplaces) { |
174 | m_shapeplaces = shapeplaces; | 151 | m_shapeplaces = shapeplaces; |
175 | } | 152 | } |
@@ -177,41 +154,144 @@ void Shape::setPlaces(int shapeplaces) { | |||
177 | void Shape::update() { | 154 | void Shape::update() { |
178 | if (m_win == 0 || m_win->window() == 0) | 155 | if (m_win == 0 || m_win->window() == 0) |
179 | return; | 156 | return; |
157 | |||
180 | #ifdef SHAPE | 158 | #ifdef SHAPE |
181 | if (m_clipshape.get() == 0 || | 159 | /** |
182 | m_win->width() != clipWidth() || | 160 | * Set the client's shape in position, |
183 | m_win->height() != clipHeight()) { | 161 | * or wipe the shape and return. |
184 | m_clipshape.reset(createShape(true)); | 162 | */ |
163 | Display *display = FbTk::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; | ||
185 | } | 179 | } |
186 | if (m_boundingshape.get() == 0 || | ||
187 | (m_win->width()+m_win->borderWidth()*2) != width() || | ||
188 | (m_win->height()+m_win->borderWidth()*2) != height()) { | ||
189 | if (m_win->borderWidth() != 0) | ||
190 | m_boundingshape.reset(createShape(false)); | ||
191 | else | ||
192 | m_boundingshape.reset(0); | ||
193 | 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); | ||
194 | } | 257 | } |
195 | 258 | ||
196 | // the m_shape can be = 0 which will just raeset the shape mask | ||
197 | // and make the window normal | ||
198 | XShapeCombineMask(FbTk::App::instance()->display(), | ||
199 | m_win->window(), | ||
200 | ShapeClip, | ||
201 | 0, 0, | ||
202 | (m_clipshape.get() != 0)? m_clipshape->drawable() : 0, | ||
203 | ShapeSet); | ||
204 | |||
205 | // the m_shape can be = 0 which will just raeset the shape mask | ||
206 | // and make the window normal | ||
207 | XShapeCombineMask(FbTk::App::instance()->display(), | ||
208 | m_win->window(), | ||
209 | ShapeBounding, | ||
210 | -m_win->borderWidth(), -m_win->borderWidth(), | ||
211 | (m_boundingshape.get() != 0)? m_boundingshape->drawable() : | ||
212 | ((m_clipshape.get() != 0)? m_clipshape->drawable(): 0), | ||
213 | ShapeSet); | ||
214 | 259 | ||
260 | CornerPixmaps &corners = s_corners[m_win->screenNumber()]; | ||
261 | #define SHAPECORNER(corner, x, y, shapekind) \ | ||
262 | XShapeCombineMask(FbTk::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 | } | ||
215 | 295 | ||
216 | #endif // SHAPE | 296 | #endif // SHAPE |
217 | 297 | ||
@@ -222,6 +302,41 @@ void Shape::setWindow(FbTk::FbWindow &win) { | |||
222 | update(); | 302 | update(); |
223 | } | 303 | } |
224 | 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(FbTk::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 | |||
225 | void Shape::setShapeNotify(const FbTk::FbWindow &win) { | 340 | void Shape::setShapeNotify(const FbTk::FbWindow &win) { |
226 | #ifdef SHAPE | 341 | #ifdef SHAPE |
227 | XShapeSelectInput(FbTk::App::instance()->display(), | 342 | XShapeSelectInput(FbTk::App::instance()->display(), |
@@ -248,18 +363,3 @@ bool Shape::isShaped(const FbTk::FbWindow &win) { | |||
248 | return (shaped != 0 ? true : false); | 363 | return (shaped != 0 ? true : false); |
249 | } | 364 | } |
250 | 365 | ||
251 | unsigned int Shape::width() const { | ||
252 | return m_boundingshape.get() ? m_boundingshape->width() : 0; | ||
253 | } | ||
254 | |||
255 | unsigned int Shape::height() const { | ||
256 | return m_boundingshape.get() ? m_boundingshape->height() : 0; | ||
257 | } | ||
258 | |||
259 | unsigned int Shape::clipWidth() const { | ||
260 | return m_clipshape.get() ? m_clipshape->width() : 0; | ||
261 | } | ||
262 | |||
263 | unsigned int Shape::clipHeight() const { | ||
264 | return m_clipshape.get() ? m_clipshape->height() : 0; | ||
265 | } | ||