aboutsummaryrefslogtreecommitdiff
path: root/src/Shape.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/Shape.cc')
-rw-r--r--src/Shape.cc334
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
49using std::min; 49using std::min;
50 50
51// ignore_border basically means do clip shape instead of bounding shape 51namespace {
52FbTk::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 || 53Pixmap 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
97std::vector<Shape::CornerPixmaps> Shape::s_corners;
98
141Shape::Shape(FbTk::FbWindow &win, int shapeplaces): 99Shape::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
149Shape::~Shape() { 113Shape::~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
134void 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
173void Shape::setPlaces(int shapeplaces) { 150void Shape::setPlaces(int shapeplaces) {
174 m_shapeplaces = shapeplaces; 151 m_shapeplaces = shapeplaces;
175} 152}
@@ -177,41 +154,144 @@ void Shape::setPlaces(int shapeplaces) {
177void Shape::update() { 154void 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 */
320void 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
334void Shape::setShapeOffsets(int xoff, int yoff) {
335 m_shapesource_xoff = xoff;
336 m_shapesource_yoff = yoff;
337 update();
338}
339
225void Shape::setShapeNotify(const FbTk::FbWindow &win) { 340void 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
251unsigned int Shape::width() const {
252 return m_boundingshape.get() ? m_boundingshape->width() : 0;
253}
254
255unsigned int Shape::height() const {
256 return m_boundingshape.get() ? m_boundingshape->height() : 0;
257}
258
259unsigned int Shape::clipWidth() const {
260 return m_clipshape.get() ? m_clipshape->width() : 0;
261}
262
263unsigned int Shape::clipHeight() const {
264 return m_clipshape.get() ? m_clipshape->height() : 0;
265}