Merge lp:~uriboni/unity-2d/spread-safer-window-image-provider into lp:unity-2d/3.0

Proposed by Ugo Riboni
Status: Merged
Approved by: Florian Boucault
Approved revision: 536
Merged at revision: 541
Proposed branch: lp:~uriboni/unity-2d/spread-safer-window-image-provider
Merge into: lp:unity-2d/3.0
Diff against target: 169 lines (+76/-35)
2 files modified
libunity-2d-private/Unity2d/windowimageprovider.cpp (+71/-35)
libunity-2d-private/Unity2d/windowimageprovider.h (+5/-0)
To merge this branch: bzr merge lp:~uriboni/unity-2d/spread-safer-window-image-provider
Reviewer Review Type Date Requested Status
Florian Boucault (community) functional Approve
Review via email: mp+57856@code.launchpad.net

Commit message

[spread] Implement a safer logic to convert the captured window pixmap into images that is resistant to the window being unmapped while we convert.

Description of the change

This MR makes sure that if the conversion of the captured window pixmap to fails due to the window being unmapped while we're converting it, we don't crash (and we retry to grab the pixmap following the alternative code path for unmapped windows).

The reason for the crash is explained in large detail in the attached bug.

To test this patch I suggest you do *not* apply the fix in lp:~uriboni/unity-2d/spread-no-windows-when-inactive , then follow the instructions in the bug report and verify that no matter how many times you try you don't get the crash.
A more scientific approach (given the fact that the bug is not reproducible reliably, but only randomly after usually two or three tries) would be to go in with a debugger and verify that we're actually at least once failing to convert the image and recover safely from that.

To post a comment you must log in.
Revision history for this message
Florian Boucault (fboucault) wrote :

Functionally fine, fixes the crasher and does not introduce regressions.

review: Approve (functional)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'libunity-2d-private/Unity2d/windowimageprovider.cpp'
2--- libunity-2d-private/Unity2d/windowimageprovider.cpp 2011-02-04 18:00:55 +0000
3+++ libunity-2d-private/Unity2d/windowimageprovider.cpp 2011-04-15 11:52:29 +0000
4@@ -109,16 +109,43 @@
5 /* After doing this, split the rest of the id on the character "|". The first
6 part is the window ID of the decorations, the latter of the actual content. */
7 atPos = windowIds.indexOf('|');
8- QString frameId = (atPos == -1) ? windowIds : windowIds.left(atPos);
9- QString contentId = (atPos == -1) ? windowIds : windowIds.mid(atPos + 1);
10-
11- QPixmap shot;
12+ Window frameId = ((atPos == -1) ? windowIds : windowIds.left(atPos)).toULong();
13+ Window contentId = ((atPos == -1) ? windowIds : windowIds.mid(atPos + 1)).toULong();
14+
15+ QImage image;
16+ QPixmap pixmap = getWindowPixmap(frameId, contentId);
17+ if (!pixmap.isNull()) {
18+ image = convertWindowPixmap(pixmap, frameId);
19+ if (image.isNull()) {
20+ /* This means that the window got unmapped while we were converting it to
21+ an image. Try again so that we will pick up the pixmap captured by
22+ metacity instead. */
23+ pixmap = getWindowPixmap(frameId, contentId);
24+ if (!pixmap.isNull()) {
25+ image = convertWindowPixmap(pixmap, frameId);
26+ }
27+ }
28+ }
29+
30+ if (!image.isNull()) {
31+ if (requestedSize.isValid()) {
32+ image = image.scaled(requestedSize);
33+ }
34+ size->setWidth(image.width());
35+ size->setHeight(image.height());
36+ }
37+
38+ return image;
39+}
40+
41+QPixmap WindowImageProvider::getWindowPixmap(Window frameWindowId,
42+ Window contentWindowId)
43+{
44 XWindowAttributes attr;
45- Window frame = (Window) frameId.toULong();
46
47- XGetWindowAttributes(QX11Info::display(), frame, &attr);
48+ XGetWindowAttributes(QX11Info::display(), frameWindowId, &attr);
49 if (attr.map_state == IsViewable) {
50- shot = QPixmap::fromX11Pixmap(frame);
51+ return QPixmap::fromX11Pixmap(frameWindowId);
52 } else {
53 /* If the window is not viewable, grabbing the pixmap directly will fail.
54 Therefore we try to retrieve the captured pixmap from the special metacity
55@@ -127,20 +154,25 @@
56 window including decorations.
57 */
58 XID pixmap;
59- Window content = (Window) contentId.toULong();
60- if (!tryGetWindowCapture(content, &pixmap)) {
61- return QImage();
62- }
63- else {
64- shot = QPixmap::fromX11Pixmap(pixmap);
65- }
66- }
67-
68- if (shot.isNull()) {
69- return QImage();
70- }
71-
72- QImage result;
73+ if (!tryGetWindowCapture(contentWindowId, &pixmap)) {
74+ return QPixmap();
75+ } else {
76+ return QPixmap::fromX11Pixmap(pixmap);
77+ }
78+ }
79+}
80+
81+/* All the exception checks in this method are due to the fact that
82+ the windowPixmap is an X11 Drawable tied to a window. When we called
83+ this function the drawable was valid since the window was mapped, however
84+ there's no guarantee it will stay that way.
85+ All the operations we do here involve at some point converting the pixmap
86+ into a QImage (since we use the rester paint engine). This conversion will
87+ throw std::bad_alloc if the drawable is not valid, so we need to catch it.
88+*/
89+QImage WindowImageProvider::convertWindowPixmap(QPixmap windowPixmap,
90+ Window frameWindowId)
91+{
92 if (m_x11supportsShape) {
93 /* The borders of the window may be irregularly shaped.
94 To capture this correctly we need to ask the Shape extension
95@@ -150,34 +182,38 @@
96 XRectangle *rectangles;
97 int rectangle_count, rectangle_order;
98 rectangles = XShapeGetRectangles (QX11Info::display(),
99- frame,
100+ frameWindowId,
101 ShapeBounding,
102 &rectangle_count,
103 &rectangle_order);
104
105- result = QImage(shot.size(), QImage::Format_ARGB32_Premultiplied);
106+ QImage result(windowPixmap.size(), QImage::Format_ARGB32_Premultiplied);
107 result.fill(Qt::transparent);
108 QPainter painter(&result);
109
110 for (int i = 0; i < rectangle_count; i++) {
111 XRectangle r = rectangles[i];
112- painter.drawPixmap(QPointF(r.x, r.y), shot,
113- QRectF(r.x, r.y, r.width, r.height));
114+ try {
115+ /* Since we're using the raster paint engine, this will internally
116+ call QX11PixmapData::toImage on windowPixmap to be able to
117+ draw it on the QImage */
118+ painter.drawPixmap(QPointF(r.x, r.y), windowPixmap,
119+ QRectF(r.x, r.y, r.width, r.height));
120+ } catch (std::bad_alloc) {
121+ return QImage();
122+ }
123 }
124
125 painter.end();
126 XFree(rectangles);
127+ return result;
128 } else {
129- result = shot.toImage();
130- }
131-
132- if (requestedSize.isValid()) {
133- result = result.scaled(requestedSize);
134- }
135- size->setWidth(result.width());
136- size->setHeight(result.height());
137-
138- return result;
139+ try {
140+ return windowPixmap.toImage();
141+ } catch (std::bad_alloc) {
142+ return QImage();
143+ }
144+ }
145 }
146
147 /*! Tries to ask the X Composite extension (if supported) to redirect all
148
149=== modified file 'libunity-2d-private/Unity2d/windowimageprovider.h'
150--- libunity-2d-private/Unity2d/windowimageprovider.h 2011-01-27 09:30:14 +0000
151+++ libunity-2d-private/Unity2d/windowimageprovider.h 2011-04-15 11:52:29 +0000
152@@ -21,6 +21,8 @@
153 #include <QImage>
154 #include <QSize>
155
156+typedef unsigned long Window;
157+
158 class WindowImageProvider : public QDeclarativeImageProvider
159 {
160 public:
161@@ -30,6 +32,9 @@
162 static void activateComposite();
163
164 private:
165+ QPixmap getWindowPixmap(Window frameWindowId, Window contentWindowId);
166+ QImage convertWindowPixmap(QPixmap windowPixmap, Window frameWindowId);
167+
168 bool m_x11supportsShape;
169 };
170

Subscribers

People subscribed via source and target branches

to all changes: