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 | |
parent | a04eed16c5287c11e565ecb25f465b96d6f61279 (diff) | |
download | fluxbox_pavel-a0f44b9e9a7c2e401e2cf1ef80fed98acf1d7e44.zip fluxbox_pavel-a0f44b9e9a7c2e401e2cf1ef80fed98acf1d7e44.tar.bz2 |
Fix handling of Shape, stage 2 (more involved/complete handling)
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | src/FbWinFrame.cc | 39 | ||||
-rw-r--r-- | src/FbWinFrame.hh | 7 | ||||
-rw-r--r-- | src/Shape.cc | 334 | ||||
-rw-r--r-- | src/Shape.hh | 26 | ||||
-rw-r--r-- | src/Window.cc | 64 | ||||
-rw-r--r-- | src/Window.hh | 3 |
7 files changed, 279 insertions, 200 deletions
@@ -1,6 +1,12 @@ | |||
1 | (Format: Year/Month/Day) | 1 | (Format: Year/Month/Day) |
2 | Changes for 1.0.0: | 2 | Changes for 1.0.0: |
3 | *07/08/09: | 3 | *07/08/09: |
4 | * Fix shaping handling, stage 2, (Simon) | ||
5 | - rewrite the core of the Shape handling so that it properly | ||
6 | merges client and frame shapes. Fixes all sorts of odd shaping | ||
7 | behaviour, and incidentally xeyes now gets a visible frame | ||
8 | (not having the frame was actually a bug). | ||
9 | Shape.hh/cc FbWinFrame.hh/cc Window.hh/cc | ||
4 | * Fix shaping handling, stage 1, (Simon) | 10 | * Fix shaping handling, stage 1, (Simon) |
5 | - do borders properly with rounded corners | 11 | - do borders properly with rounded corners |
6 | - propagate client clip mask as well as bounding mask | 12 | - propagate client clip mask as well as bounding mask |
diff --git a/src/FbWinFrame.cc b/src/FbWinFrame.cc index d3b3d2f..fbf1102 100644 --- a/src/FbWinFrame.cc +++ b/src/FbWinFrame.cc | |||
@@ -96,8 +96,8 @@ FbWinFrame::FbWinFrame(BScreen &screen, FbWinFrameTheme &theme, FbTk::ImageContr | |||
96 | m_unfocused_alpha(0), | 96 | m_unfocused_alpha(0), |
97 | m_double_click_time(0), | 97 | m_double_click_time(0), |
98 | m_themelistener(*this), | 98 | m_themelistener(*this), |
99 | m_shape(new Shape(m_window, theme.shapePlace())), | 99 | m_shape(m_window, theme.shapePlace()), |
100 | m_disable_shape(false) { | 100 | m_disable_themeshape(false) { |
101 | m_theme.reconfigSig().attach(&m_themelistener); | 101 | m_theme.reconfigSig().attach(&m_themelistener); |
102 | init(); | 102 | init(); |
103 | } | 103 | } |
@@ -210,7 +210,7 @@ void FbWinFrame::show() { | |||
210 | 210 | ||
211 | if (m_need_render) { | 211 | if (m_need_render) { |
212 | renderAll(); | 212 | renderAll(); |
213 | applyAll(); | 213 | applyAll(); |
214 | clearAll(); | 214 | clearAll(); |
215 | } | 215 | } |
216 | 216 | ||
@@ -235,8 +235,7 @@ void FbWinFrame::shade() { | |||
235 | m_window.resize(m_window.width(), m_titlebar.height()); | 235 | m_window.resize(m_window.width(), m_titlebar.height()); |
236 | alignTabs(); | 236 | alignTabs(); |
237 | // need to update our shape | 237 | // need to update our shape |
238 | if ( m_shape.get() ) | 238 | m_shape.update(); |
239 | m_shape->update(); | ||
240 | } else { // should be unshaded | 239 | } else { // should be unshaded |
241 | m_window.resize(m_window.width(), m_height_before_shade); | 240 | m_window.resize(m_window.width(), m_height_before_shade); |
242 | reconfigure(); | 241 | reconfigure(); |
@@ -1122,26 +1121,28 @@ void FbWinFrame::reconfigure() { | |||
1122 | m_need_render = true; | 1121 | m_need_render = true; |
1123 | } | 1122 | } |
1124 | 1123 | ||
1125 | if (m_shape.get() && theme().shapePlace() == Shape::NONE || m_disable_shape) | 1124 | if (m_disable_themeshape) |
1126 | m_shape.reset(0); | 1125 | m_shape.setPlaces(Shape::NONE); |
1127 | else if (m_shape.get() == 0 && theme().shapePlace() != Shape::NONE) | 1126 | else |
1128 | m_shape.reset(new Shape(window(), theme().shapePlace())); | 1127 | m_shape.setPlaces(theme().shapePlace()); |
1129 | else if (m_shape.get()) | ||
1130 | m_shape->setPlaces(theme().shapePlace()); | ||
1131 | 1128 | ||
1132 | if (m_shape.get()) | 1129 | m_shape.setShapeOffsets(0, titlebarHeight()); |
1133 | m_shape->update(); | ||
1134 | 1130 | ||
1135 | // titlebar stuff rendered already by reconftitlebar | 1131 | // titlebar stuff rendered already by reconftitlebar |
1136 | } | 1132 | } |
1137 | 1133 | ||
1138 | void FbWinFrame::setUseShape(bool value) { | 1134 | void FbWinFrame::setUseShape(bool value) { |
1139 | m_disable_shape = !value; | 1135 | m_disable_themeshape = !value; |
1136 | |||
1137 | if (m_disable_themeshape) | ||
1138 | m_shape.setPlaces(Shape::NONE); | ||
1139 | else | ||
1140 | m_shape.setPlaces(theme().shapePlace()); | ||
1141 | |||
1142 | } | ||
1140 | 1143 | ||
1141 | if (m_shape.get() && m_disable_shape) | 1144 | void FbWinFrame::setShapingClient(FbTk::FbWindow *win, bool always_update) { |
1142 | m_shape.reset(0); | 1145 | m_shape.setShapeSource(win, 0, titlebarHeight(), always_update); |
1143 | else if (m_shape.get() == 0 && !m_disable_shape) | ||
1144 | m_shape.reset(new Shape(window(), theme().shapePlace())); | ||
1145 | } | 1146 | } |
1146 | 1147 | ||
1147 | unsigned int FbWinFrame::buttonHeight() const { | 1148 | unsigned int FbWinFrame::buttonHeight() const { |
@@ -1452,7 +1453,7 @@ void FbWinFrame::init() { | |||
1452 | if (theme().handleWidth() == 0) | 1453 | if (theme().handleWidth() == 0) |
1453 | m_use_handle = false; | 1454 | m_use_handle = false; |
1454 | 1455 | ||
1455 | m_disable_shape = false; | 1456 | m_disable_themeshape = false; |
1456 | 1457 | ||
1457 | m_current_label = 0; // no focused button at first | 1458 | m_current_label = 0; // no focused button at first |
1458 | 1459 | ||
diff --git a/src/FbWinFrame.hh b/src/FbWinFrame.hh index 87c290c..1eefe42 100644 --- a/src/FbWinFrame.hh +++ b/src/FbWinFrame.hh | |||
@@ -33,6 +33,7 @@ | |||
33 | #include "FbTk/XLayerItem.hh" | 33 | #include "FbTk/XLayerItem.hh" |
34 | #include "FbTk/TextButton.hh" | 34 | #include "FbTk/TextButton.hh" |
35 | #include "Container.hh" | 35 | #include "Container.hh" |
36 | #include "Shape.hh" | ||
36 | 37 | ||
37 | #include <vector> | 38 | #include <vector> |
38 | #include <list> | 39 | #include <list> |
@@ -197,6 +198,8 @@ public: | |||
197 | 198 | ||
198 | void reconfigure(); | 199 | void reconfigure(); |
199 | void setUseShape(bool value); | 200 | void setUseShape(bool value); |
201 | void setShapingClient(FbTk::FbWindow *win, bool always_update); | ||
202 | void updateShape() { m_shape.update(); } | ||
200 | 203 | ||
201 | /** | 204 | /** |
202 | @name accessors | 205 | @name accessors |
@@ -403,9 +406,9 @@ private: | |||
403 | FbWinFrame &m_frame; | 406 | FbWinFrame &m_frame; |
404 | }; | 407 | }; |
405 | ThemeListener m_themelistener; | 408 | ThemeListener m_themelistener; |
406 | std::auto_ptr<Shape> m_shape; | 409 | Shape m_shape; |
407 | bool m_disable_shape; | ||
408 | 410 | ||
411 | bool m_disable_themeshape; | ||
409 | }; | 412 | }; |
410 | 413 | ||
411 | #endif // FBWINFRAME_HH | 414 | #endif // FBWINFRAME_HH |
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 | } | ||
diff --git a/src/Shape.hh b/src/Shape.hh index c8db79a..5f26d05 100644 --- a/src/Shape.hh +++ b/src/Shape.hh | |||
@@ -24,12 +24,14 @@ | |||
24 | #ifndef SHAPE_HH | 24 | #ifndef SHAPE_HH |
25 | #define SHAPE_HH | 25 | #define SHAPE_HH |
26 | 26 | ||
27 | #include "FbTk/FbPixmap.hh" | ||
28 | |||
27 | #include <X11/Xlib.h> | 29 | #include <X11/Xlib.h> |
28 | #include <memory> | 30 | #include <memory> |
31 | #include <vector> | ||
29 | 32 | ||
30 | namespace FbTk { | 33 | namespace FbTk { |
31 | class FbWindow; | 34 | class FbWindow; |
32 | class FbPixmap; | ||
33 | } | 35 | } |
34 | 36 | ||
35 | /// creates round corners on windows | 37 | /// creates round corners on windows |
@@ -51,6 +53,10 @@ public: | |||
51 | void update(); | 53 | void update(); |
52 | /// assign a new window | 54 | /// assign a new window |
53 | void setWindow(FbTk::FbWindow &win); | 55 | void setWindow(FbTk::FbWindow &win); |
56 | /// Assign a window to merge our shape with. | ||
57 | /// (note that this is currently specific to frames) | ||
58 | void setShapeSource(FbTk::FbWindow *win, int xoff, int yoff, bool always_update); | ||
59 | void setShapeOffsets(int xoff, int yoff); | ||
54 | unsigned int width() const; | 60 | unsigned int width() const; |
55 | unsigned int height() const; | 61 | unsigned int height() const; |
56 | unsigned int clipWidth() const; | 62 | unsigned int clipWidth() const; |
@@ -60,11 +66,21 @@ public: | |||
60 | /// @return true if window has shape | 66 | /// @return true if window has shape |
61 | static bool isShaped(const FbTk::FbWindow &win); | 67 | static bool isShaped(const FbTk::FbWindow &win); |
62 | private: | 68 | private: |
63 | FbTk::FbPixmap *createShape(bool clipshape); // true for clip, false for bounding | ||
64 | |||
65 | FbTk::FbWindow *m_win; ///< window to be shaped | 69 | FbTk::FbWindow *m_win; ///< window to be shaped |
66 | std::auto_ptr<FbTk::FbPixmap> m_clipshape; ///< our shape pixmap | 70 | FbTk::FbWindow *m_shapesource; ///< window to pull shape from |
67 | std::auto_ptr<FbTk::FbPixmap> m_boundingshape; ///< our shape pixmap | 71 | int m_shapesource_xoff, m_shapesource_yoff; |
72 | |||
73 | void initCorners(int screen_num); | ||
74 | |||
75 | struct CornerPixmaps { | ||
76 | FbTk::FbPixmap topleft; | ||
77 | FbTk::FbPixmap topright; | ||
78 | FbTk::FbPixmap botleft; | ||
79 | FbTk::FbPixmap botright; | ||
80 | }; | ||
81 | |||
82 | // unfortunately, we need a separate pixmap per screen | ||
83 | static std::vector<CornerPixmaps> s_corners; | ||
68 | int m_shapeplaces; ///< places to shape | 84 | int m_shapeplaces; ///< places to shape |
69 | 85 | ||
70 | }; | 86 | }; |
diff --git a/src/Window.cc b/src/Window.cc index ab917da..9ee1c5e 100644 --- a/src/Window.cc +++ b/src/Window.cc | |||
@@ -268,7 +268,6 @@ FluxboxWindow::FluxboxWindow(WinClient &client, FbWinFrameTheme &tm, | |||
268 | m_old_decoration_mask(0), | 268 | m_old_decoration_mask(0), |
269 | m_client(&client), | 269 | m_client(&client), |
270 | m_toggled_decos(false), | 270 | m_toggled_decos(false), |
271 | m_shaped(false), | ||
272 | m_icon_hidden(false), | 271 | m_icon_hidden(false), |
273 | m_focus_hidden(false), | 272 | m_focus_hidden(false), |
274 | m_old_pos_x(0), m_old_pos_y(0), | 273 | m_old_pos_x(0), m_old_pos_y(0), |
@@ -350,16 +349,10 @@ void FluxboxWindow::init() { | |||
350 | m_client->setFluxboxWindow(this); | 349 | m_client->setFluxboxWindow(this); |
351 | m_client->setGroupLeftWindow(None); // nothing to the left. | 350 | m_client->setGroupLeftWindow(None); // nothing to the left. |
352 | 351 | ||
353 | // check for shape extension and whether the window is shaped | ||
354 | m_shaped = false; | ||
355 | |||
356 | if (Fluxbox::instance()->haveShape()) { | 352 | if (Fluxbox::instance()->haveShape()) { |
357 | Shape::setShapeNotify(winClient()); | 353 | Shape::setShapeNotify(winClient()); |
358 | m_shaped = Shape::isShaped(winClient()); | ||
359 | } | 354 | } |
360 | 355 | ||
361 | frame().setUseShape(!m_shaped); | ||
362 | |||
363 | //!! TODO init of client should be better | 356 | //!! TODO init of client should be better |
364 | // we don't want to duplicate code here and in attachClient | 357 | // we don't want to duplicate code here and in attachClient |
365 | m_clientlist.push_back(m_client); | 358 | m_clientlist.push_back(m_client); |
@@ -548,36 +541,12 @@ void FluxboxWindow::init() { | |||
548 | // no focus default | 541 | // no focus default |
549 | setFocusFlag(false); | 542 | setFocusFlag(false); |
550 | 543 | ||
551 | if (m_shaped) | ||
552 | shape(); | ||
553 | |||
554 | setupWindow(); | 544 | setupWindow(); |
555 | 545 | ||
556 | FbTk::App::instance()->sync(false); | 546 | FbTk::App::instance()->sync(false); |
557 | 547 | ||
558 | } | 548 | } |
559 | 549 | ||
560 | /// apply shape to this window | ||
561 | void FluxboxWindow::shape() { | ||
562 | #ifdef SHAPE | ||
563 | if (m_shaped) { | ||
564 | XShapeCombineShape(display, | ||
565 | frame().window().window(), ShapeClip, | ||
566 | 0, frame().clientArea().y(), // xOff, yOff | ||
567 | m_client->window(), | ||
568 | ShapeClip, ShapeSet); | ||
569 | XShapeCombineShape(display, | ||
570 | frame().window().window(), ShapeBounding, | ||
571 | 0, frame().clientArea().y(), // xOff, yOff | ||
572 | m_client->window(), | ||
573 | ShapeBounding, ShapeSet); | ||
574 | XFlush(display); | ||
575 | } | ||
576 | #endif // SHAPE | ||
577 | |||
578 | } | ||
579 | |||
580 | |||
581 | /// attach a client to this window and destroy old window | 550 | /// attach a client to this window and destroy old window |
582 | void FluxboxWindow::attachClient(WinClient &client, int x, int y) { | 551 | void FluxboxWindow::attachClient(WinClient &client, int x, int y) { |
583 | //!! TODO: check for isGroupable in client | 552 | //!! TODO: check for isGroupable in client |
@@ -995,8 +964,10 @@ bool FluxboxWindow::setCurrentClient(WinClient &client, bool setinput) { | |||
995 | if (!button) | 964 | if (!button) |
996 | return false; | 965 | return false; |
997 | 966 | ||
998 | if (&client != m_client) | 967 | if (&client != m_client) { |
999 | m_screen.focusControl().setScreenFocusedWindow(client); | 968 | m_screen.focusControl().setScreenFocusedWindow(client); |
969 | frame().setShapingClient(&client, false); | ||
970 | } | ||
1000 | m_client = &client; | 971 | m_client = &client; |
1001 | m_client->raise(); | 972 | m_client->raise(); |
1002 | m_client->focusSig().notify(); | 973 | m_client->focusSig().notify(); |
@@ -1042,6 +1013,8 @@ void FluxboxWindow::associateClientWindow(bool use_attrs, | |||
1042 | updateTitleFromClient(*m_client); | 1013 | updateTitleFromClient(*m_client); |
1043 | updateIconNameFromClient(*m_client); | 1014 | updateIconNameFromClient(*m_client); |
1044 | 1015 | ||
1016 | frame().setShapingClient(m_client, false); | ||
1017 | |||
1045 | if (use_attrs) | 1018 | if (use_attrs) |
1046 | frame().moveResizeForClient(x, y, | 1019 | frame().moveResizeForClient(x, y, |
1047 | width, height, gravity, client_bw); | 1020 | width, height, gravity, client_bw); |
@@ -1299,7 +1272,6 @@ void FluxboxWindow::moveResize(int new_x, int new_y, | |||
1299 | sendConfigureNotify(); | 1272 | sendConfigureNotify(); |
1300 | } | 1273 | } |
1301 | 1274 | ||
1302 | shape(); | ||
1303 | 1275 | ||
1304 | if (!moving) { | 1276 | if (!moving) { |
1305 | m_last_resize_x = new_x; | 1277 | m_last_resize_x = new_x; |
@@ -1319,8 +1291,6 @@ void FluxboxWindow::moveResizeForClient(int new_x, int new_y, | |||
1319 | shaded = false; | 1291 | shaded = false; |
1320 | sendConfigureNotify(); | 1292 | sendConfigureNotify(); |
1321 | 1293 | ||
1322 | shape(); | ||
1323 | |||
1324 | if (!moving) { | 1294 | if (!moving) { |
1325 | m_last_resize_x = new_x; | 1295 | m_last_resize_x = new_x; |
1326 | m_last_resize_y = new_y; | 1296 | m_last_resize_y = new_y; |
@@ -1617,7 +1587,7 @@ void FluxboxWindow::setFullscreen(bool flag) { | |||
1617 | 1587 | ||
1618 | fullscreen = false; | 1588 | fullscreen = false; |
1619 | 1589 | ||
1620 | frame().setUseShape(!m_shaped); | 1590 | frame().setUseShape(true); |
1621 | if (m_toggled_decos) { | 1591 | if (m_toggled_decos) { |
1622 | if (m_old_decoration_mask & DECORM_TITLEBAR) | 1592 | if (m_old_decoration_mask & DECORM_TITLEBAR) |
1623 | setDecoration(DECOR_NONE); | 1593 | setDecoration(DECOR_NONE); |
@@ -2342,24 +2312,10 @@ void FluxboxWindow::handleEvent(XEvent &event) { | |||
2342 | #endif // DEBUG | 2312 | #endif // DEBUG |
2343 | XShapeEvent *shape_event = (XShapeEvent *)&event; | 2313 | XShapeEvent *shape_event = (XShapeEvent *)&event; |
2344 | 2314 | ||
2345 | if (shape_event->kind != ShapeBounding) | 2315 | if (shape_event->shaped) |
2346 | break; | 2316 | frame().setShapingClient(m_client, true); |
2347 | 2317 | else | |
2348 | if (shape_event->shaped) { | 2318 | frame().setShapingClient(0, true); |
2349 | m_shaped = true; | ||
2350 | shape(); | ||
2351 | } else { | ||
2352 | m_shaped = false; | ||
2353 | // set no shape | ||
2354 | XShapeCombineMask(display, | ||
2355 | frame().window().window(), ShapeClip, | ||
2356 | 0, 0, | ||
2357 | None, ShapeSet); | ||
2358 | XShapeCombineMask(display, | ||
2359 | frame().window().window(), ShapeBounding, | ||
2360 | 0, 0, | ||
2361 | None, ShapeSet); | ||
2362 | } | ||
2363 | 2319 | ||
2364 | FbTk::App::instance()->sync(false); | 2320 | FbTk::App::instance()->sync(false); |
2365 | break; | 2321 | break; |
diff --git a/src/Window.hh b/src/Window.hh index 15de2bb..92431ab 100644 --- a/src/Window.hh +++ b/src/Window.hh | |||
@@ -454,8 +454,6 @@ private: | |||
454 | void updateButtons(); | 454 | void updateButtons(); |
455 | 455 | ||
456 | void init(); | 456 | void init(); |
457 | /// applies a shape mask to the window if it has one | ||
458 | void shape(); | ||
459 | void updateClientLeftWindow(); | 457 | void updateClientLeftWindow(); |
460 | void grabButtons(); | 458 | void grabButtons(); |
461 | 459 | ||
@@ -560,7 +558,6 @@ private: | |||
560 | bool resize, move, iconify, maximize, close, tabable; | 558 | bool resize, move, iconify, maximize, close, tabable; |
561 | } functions; | 559 | } functions; |
562 | 560 | ||
563 | bool m_shaped; ///< if the window is shaped with a mask | ||
564 | bool m_icon_hidden; ///< if the window is in the iconbar | 561 | bool m_icon_hidden; ///< if the window is in the iconbar |
565 | bool m_focus_hidden; ///< if the window is in the NextWindow list | 562 | bool m_focus_hidden; ///< if the window is in the NextWindow list |
566 | int m_old_pos_x, m_old_pos_y; ///< old position so we can restore from maximized | 563 | int m_old_pos_x, m_old_pos_y; ///< old position so we can restore from maximized |