~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to plasma/generic/applets/systemtray/protocols/fdo/x11embedcontainer.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   x11embedcontainer.cpp                                                 *
 
3
 *                                                                         *
 
4
 *   Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com>              *
 
5
 *                                                                         *
 
6
 *   This program is free software; you can redistribute it and/or modify  *
 
7
 *   it under the terms of the GNU General Public License as published by  *
 
8
 *   the Free Software Foundation; either version 2 of the License, or     *
 
9
 *   (at your option) any later version.                                   *
 
10
 *                                                                         *
 
11
 *   This program is distributed in the hope that it will be useful,       *
 
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 
14
 *   GNU General Public License for more details.                          *
 
15
 *                                                                         *
 
16
 *   You should have received a copy of the GNU General Public License     *
 
17
 *   along with this program; if not, write to the                         *
 
18
 *   Free Software Foundation, Inc.,                                       *
 
19
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
 
20
 ***************************************************************************/
 
21
 
 
22
#include "x11embedcontainer.h"
 
23
#include "x11embedpainter.h"
 
24
#include "fdoselectionmanager.h"
 
25
 
 
26
// KDE
 
27
#include <KDebug>
 
28
 
 
29
// Qt
 
30
#include <QtGui/QPainter>
 
31
#include <QtGui/QPaintEngine>
 
32
#include <QtGui/QX11Info>
 
33
 
 
34
// Xlib
 
35
#include <config-X11.h>
 
36
 
 
37
#include <X11/Xlib.h>
 
38
#include <X11/extensions/Xrender.h>
 
39
 
 
40
#ifdef HAVE_XCOMPOSITE
 
41
#  include <X11/extensions/Xcomposite.h>
 
42
#endif
 
43
 
 
44
 
 
45
namespace SystemTray
 
46
{
 
47
 
 
48
class X11EmbedContainer::Private
 
49
{
 
50
public:
 
51
    Private(X11EmbedContainer *q)
 
52
        : q(q),
 
53
          picture(None),
 
54
          updatesEnabled(true)
 
55
    {
 
56
    }
 
57
 
 
58
    ~Private()
 
59
    {
 
60
        if (picture) {
 
61
           XRenderFreePicture(QX11Info::display(), picture);
 
62
        }
 
63
    }
 
64
 
 
65
    X11EmbedContainer *q;
 
66
 
 
67
    XWindowAttributes attr;
 
68
    Picture picture;
 
69
    bool updatesEnabled;
 
70
    QImage oldBackgroundImage;
 
71
};
 
72
 
 
73
 
 
74
X11EmbedContainer::X11EmbedContainer(QWidget *parent)
 
75
    : QX11EmbedContainer(parent),
 
76
      d(new Private(this))
 
77
{
 
78
    connect(this, SIGNAL(clientIsEmbedded()),
 
79
            this, SLOT(ensureValidSize()));
 
80
}
 
81
 
 
82
 
 
83
X11EmbedContainer::~X11EmbedContainer()
 
84
{
 
85
    FdoSelectionManager::manager()->removeDamageWatch(this);
 
86
    delete d;
 
87
}
 
88
 
 
89
 
 
90
void X11EmbedContainer::embedSystemTrayClient(WId clientId)
 
91
{
 
92
    Display *display = QX11Info::display();
 
93
 
 
94
    if (!XGetWindowAttributes(display, clientId, &d->attr)) {
 
95
        emit error(QX11EmbedContainer::Unknown);
 
96
        return;
 
97
    }
 
98
 
 
99
    XSetWindowAttributes sAttr;
 
100
    sAttr.background_pixel = BlackPixel(display, DefaultScreen(display));
 
101
    sAttr.border_pixel = BlackPixel(display, DefaultScreen(display));
 
102
    sAttr.colormap = d->attr.colormap;
 
103
 
 
104
    WId parentId = parentWidget() ? parentWidget()->winId() : DefaultRootWindow(display);
 
105
    Window winId = XCreateWindow(display, parentId, 0, 0, d->attr.width, d->attr.height,
 
106
                                 0, d->attr.depth, InputOutput, d->attr.visual,
 
107
                                 CWBackPixel | CWBorderPixel | CWColormap, &sAttr);
 
108
 
 
109
    XWindowAttributes attr;
 
110
    if (!XGetWindowAttributes(display, winId, &attr)) {
 
111
        emit error(QX11EmbedContainer::Unknown);
 
112
        return;
 
113
    }
 
114
 
 
115
    create(winId);
 
116
 
 
117
#if defined(HAVE_XCOMPOSITE) && defined(HAVE_XFIXES) && defined(HAVE_XDAMAGE)
 
118
    XRenderPictFormat *format = XRenderFindVisualFormat(display, d->attr.visual);
 
119
    if (format && format->type == PictTypeDirect && format->direct.alphaMask &&
 
120
        FdoSelectionManager::manager()->haveComposite())
 
121
    {
 
122
        // Redirect ARGB windows to offscreen storage so we can composite them ourselves
 
123
        XRenderPictureAttributes attr;
 
124
        attr.subwindow_mode = IncludeInferiors;
 
125
 
 
126
        d->picture = XRenderCreatePicture(display, clientId, format, CPSubwindowMode, &attr);
 
127
        XCompositeRedirectSubwindows(display, winId, CompositeRedirectManual);
 
128
        FdoSelectionManager::manager()->addDamageWatch(this, clientId);
 
129
 
 
130
        //kDebug() << "Embedded client uses an ARGB visual -> compositing.";
 
131
    } else {
 
132
        //kDebug() << "Embedded client is not using an ARGB visual.";
 
133
    }
 
134
#endif
 
135
 
 
136
    // repeat everything from QX11EmbedContainer's ctor that might be relevant
 
137
    setFocusPolicy(Qt::StrongFocus);
 
138
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 
139
    setAcceptDrops(true);
 
140
    setEnabled(false);
 
141
 
 
142
    XSelectInput(display, winId,
 
143
                 KeyPressMask | KeyReleaseMask |
 
144
                 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
 
145
                 KeymapStateMask |
 
146
                 PointerMotionMask |
 
147
                 EnterWindowMask | LeaveWindowMask |
 
148
                 FocusChangeMask |
 
149
                 ExposureMask |
 
150
                 StructureNotifyMask |
 
151
                 SubstructureNotifyMask);
 
152
 
 
153
    XFlush(display);
 
154
 
 
155
    embedClient(clientId);
 
156
 
 
157
    // FIXME: This checks that the client is still valid. Qt won't pick it up
 
158
    // if the client closes before embedding completes. However, what happens
 
159
    // if the close happens after this point? Should checks happen on a timer
 
160
    // until embedding completes perhaps?
 
161
    if (!XGetWindowAttributes(QX11Info::display(), clientId, &d->attr)) {
 
162
        emit error(QX11EmbedContainer::Unknown);
 
163
        return;
 
164
    }
 
165
}
 
166
 
 
167
 
 
168
void X11EmbedContainer::ensureValidSize()
 
169
{
 
170
    QSize s = QSize(qBound(minimumSize().width(), width(), maximumSize().width()),
 
171
                    qBound(minimumSize().height(), height(), maximumSize().height()));
 
172
    resize(s);
 
173
}
 
174
 
 
175
 
 
176
void X11EmbedContainer::setUpdatesEnabled(bool enabled)
 
177
{
 
178
    d->updatesEnabled = enabled;
 
179
}
 
180
 
 
181
 
 
182
void X11EmbedContainer::paintEvent(QPaintEvent *event)
 
183
{
 
184
    Q_UNUSED(event);
 
185
 
 
186
    if (!d->updatesEnabled) {
 
187
        return;
 
188
    }
 
189
 
 
190
    if (!d->picture) {
 
191
        FdoSelectionManager::painter()->updateContainer(this);
 
192
        return;
 
193
    }
 
194
 
 
195
    // Taking a detour via a QPixmap is unfortunately the only way we can get
 
196
    // the window contents into Qt's backing store.
 
197
    QPixmap pixmap(size());
 
198
    if (pixmap.paintEngine()->type() != QPaintEngine::X11) {
 
199
#if defined(HAVE_XCOMPOSITE)
 
200
        // If we're using the raster or OpenGL graphics systems, a QPixmap isn't an X pixmap,
 
201
        // so we have to get the window contents into a QImage and then draw that.
 
202
        Display *dpy = x11Info().display();
 
203
 
 
204
        // XXX We really should keep a cached copy of the image client side, and only
 
205
        //     update it in response to a damage event.
 
206
        Pixmap pixmap = XCompositeNameWindowPixmap(dpy, clientWinId());
 
207
        XImage *ximage = XGetImage(dpy, pixmap, 0, 0, width(), height(), AllPlanes, ZPixmap);
 
208
        XFreePixmap(dpy, pixmap);
 
209
        // We actually check if we get the image from X11 since clientWinId can be any arbiter window (with crazy XWindowAttribute and the pixmap associated is bad)
 
210
        if (!ximage)
 
211
            return;
 
212
        // This is safe to do since we only composite ARGB32 windows, and PictStandardARGB32
 
213
        // matches QImage::Format_ARGB32_Premultiplied.
 
214
        QImage image((const uchar*)ximage->data, ximage->width, ximage->height, ximage->bytes_per_line,
 
215
                     QImage::Format_ARGB32_Premultiplied);
 
216
 
 
217
        QPainter p(this);
 
218
        p.drawImage(0, 0, image);
 
219
 
 
220
        XDestroyImage(ximage);
 
221
#endif
 
222
    } else {
 
223
        pixmap.fill(Qt::transparent);
 
224
 
 
225
        XRenderComposite(x11Info().display(), PictOpSrc, d->picture, None, pixmap.x11PictureHandle(),
 
226
                         0, 0, 0, 0, 0, 0, width(), height());
 
227
 
 
228
        QPainter p(this);
 
229
        p.drawPixmap(0, 0, pixmap);
 
230
    }
 
231
}
 
232
 
 
233
void X11EmbedContainer::setBackgroundPixmap(QPixmap background)
 
234
{
 
235
    if (!clientWinId()) {
 
236
        return;
 
237
    }
 
238
 
 
239
    Display *display = QX11Info::display();
 
240
    Pixmap bg = XCreatePixmap(display, clientWinId(), width(), height(), d->attr.depth);
 
241
 
 
242
    XRenderPictFormat *format = XRenderFindVisualFormat(display, d->attr.visual);
 
243
    Picture picture = XRenderCreatePicture(display, bg, format, 0, 0);
 
244
 
 
245
    //Prevent updating the background-image if possible. Updating can cause a very annoying flicker due to the XClearArea, and thus has to be kept to a minimum
 
246
    QImage image;
 
247
    if (background.paintEngine()->type() != QPaintEngine::X11)
 
248
      image = background.toImage(); // With the raster graphics system this call just returns the backing image, so the image data isn't copied.
 
249
    else
 
250
      image = background.copy().toImage(); //With the X11 graphics engine, we have to create a copy first, else we get a crash
 
251
 
 
252
    if(d->oldBackgroundImage == image) {
 
253
      XFreePixmap(display, bg);
 
254
      XRenderFreePicture(display, picture);
 
255
      return;
 
256
    }
 
257
    d->oldBackgroundImage = image;
 
258
 
 
259
    if (background.paintEngine()->type() != QPaintEngine::X11) {
 
260
 
 
261
        XRenderPictFormat *format = 0;
 
262
        int depth = 0;
 
263
        int bpp = 0;
 
264
 
 
265
        if (image.format() == QImage::Format_ARGB32_Premultiplied) {
 
266
            format = XRenderFindStandardFormat(display, PictStandardARGB32);
 
267
            depth = 32;
 
268
            bpp = 32;
 
269
        } else if (image.format() == QImage::Format_RGB32) {
 
270
            format = XRenderFindStandardFormat(display, PictStandardRGB24);
 
271
            depth = 24;
 
272
            bpp = 32;
 
273
        } else if (image.format() == QImage::Format_RGB16) {
 
274
            bpp = 16;
 
275
            depth = 16;
 
276
 
 
277
            // Try to find a picture format that matches the image format.
 
278
            // The Render spec doesn't require the X server to support 16bpp formats,
 
279
            // so this call can fail.
 
280
            XRenderPictFormat templ;
 
281
            templ.type             = PictTypeDirect;
 
282
            templ.direct.alpha     = 0;
 
283
            templ.direct.alphaMask = 0;
 
284
            templ.depth            = 16;
 
285
            templ.direct.red       = 11;
 
286
            templ.direct.redMask   = 0x1f;
 
287
            templ.direct.green     = 5;
 
288
            templ.direct.greenMask = 0x3f;
 
289
            templ.direct.blue      = 0;
 
290
            templ.direct.blueMask  = 0x1f;
 
291
            format = XRenderFindFormat(display, PictFormatType | PictFormatDepth | PictFormatAlpha |
 
292
                                       PictFormatAlphaMask | PictFormatRed | PictFormatRedMask |
 
293
                                       PictFormatGreen | PictFormatGreenMask | PictFormatBlue |
 
294
                                       PictFormatBlueMask, &templ, 0);
 
295
        }
 
296
 
 
297
        if (format == 0)
 
298
        {
 
299
            // Convert the image to a standard format.
 
300
            if (image.hasAlphaChannel()) {
 
301
                image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
 
302
                format = XRenderFindStandardFormat(display, PictStandardARGB32);
 
303
                depth = 32;
 
304
            } else { 
 
305
                image = image.convertToFormat(QImage::Format_RGB32);
 
306
                format = XRenderFindStandardFormat(display, PictStandardRGB24);
 
307
                depth = 24;
 
308
            }
 
309
            bpp = 32;
 
310
        }
 
311
 
 
312
        if (image.format() == QImage::Format_RGB32) {
 
313
            // Make sure the would be alpha bits are set to 1.
 
314
            quint32 * const pixels = (quint32*)(const_cast<const QImage*>(&image)->bits());
 
315
            for (int i = 0; i < image.width() * image.height(); i++) {
 
316
                pixels[i] |= 0xff000000;
 
317
            }
 
318
        }
 
319
 
 
320
        Q_ASSERT(format != 0);
 
321
 
 
322
        // Get the image data into a pixmap
 
323
        XImage ximage;
 
324
        ximage.width            = image.width(); 
 
325
        ximage.height           = image.height();
 
326
        ximage.xoffset          = 0; 
 
327
        ximage.format           = ZPixmap;
 
328
        // This is a hack to prevent the image data from detaching
 
329
        ximage.data             = (char*) const_cast<const QImage*>(&image)->bits();
 
330
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
 
331
        ximage.byte_order       = MSBFirst;
 
332
#else
 
333
        ximage.byte_order       = LSBFirst;
 
334
#endif
 
335
        ximage.bitmap_unit      = bpp;
 
336
        ximage.bitmap_bit_order = ximage.byte_order;
 
337
        ximage.bitmap_pad       = bpp;
 
338
        ximage.depth            = depth;
 
339
        ximage.bytes_per_line   = image.bytesPerLine();
 
340
        ximage.bits_per_pixel   = bpp;
 
341
        if (depth > 16) {
 
342
            ximage.red_mask     = 0x00ff0000;
 
343
            ximage.green_mask   = 0x0000ff00;
 
344
            ximage.blue_mask    = 0x000000ff;
 
345
        } else {
 
346
            // r5g6b5
 
347
            ximage.red_mask     = 0xf800;
 
348
            ximage.green_mask   = 0x07e0;
 
349
            ximage.blue_mask    = 0x001f;
 
350
        }
 
351
        ximage.obdata           = 0;
 
352
        if (XInitImage(&ximage) == 0) {
 
353
            XRenderFreePicture(display, picture);
 
354
            XFreePixmap(display, bg);
 
355
            return;
 
356
        }
 
357
 
 
358
        Pixmap pm = XCreatePixmap(display, clientWinId(), width(), height(), ximage.depth);
 
359
        GC gc = XCreateGC(display, pm, 0, 0);
 
360
        XPutImage(display, pm, gc, &ximage, 0, 0, 0, 0, width(), height());
 
361
        XFreeGC(display, gc);
 
362
 
 
363
        Picture pict = XRenderCreatePicture(display, pm, format, 0, 0);
 
364
        XRenderComposite(display, PictOpSrc, pict, None, picture,
 
365
                         0, 0, 0, 0, 0, 0, width(), height());
 
366
        XRenderFreePicture(display, pict);
 
367
        XFreePixmap(display, pm);
 
368
    } else {
 
369
        XRenderComposite(display, PictOpSrc, background.x11PictureHandle(),
 
370
                         None, picture, 0, 0, 0, 0, 0, 0, width(), height());
 
371
    }
 
372
 
 
373
    XSetWindowBackgroundPixmap(display, clientWinId(), bg);
 
374
 
 
375
    XRenderFreePicture(display, picture);
 
376
    XFreePixmap(display, bg);
 
377
 
 
378
    XClearArea(display, clientWinId(), 0, 0, 0, 0, True);
 
379
}
 
380
 
 
381
}
 
382
 
 
383
#include "x11embedcontainer.moc"