1
/***************************************************************************
2
* x11embedcontainer.cpp *
4
* Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
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. *
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. *
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
***************************************************************************/
22
#include "x11embedcontainer.h"
23
#include "x11embedpainter.h"
24
#include "fdoselectionmanager.h"
30
#include <QtGui/QPainter>
31
#include <QtGui/QPaintEngine>
32
#include <QtGui/QX11Info>
35
#include <config-X11.h>
38
#include <X11/extensions/Xrender.h>
40
#ifdef HAVE_XCOMPOSITE
41
# include <X11/extensions/Xcomposite.h>
48
class X11EmbedContainer::Private
51
Private(X11EmbedContainer *q)
61
XRenderFreePicture(QX11Info::display(), picture);
67
XWindowAttributes attr;
70
QImage oldBackgroundImage;
74
X11EmbedContainer::X11EmbedContainer(QWidget *parent)
75
: QX11EmbedContainer(parent),
78
connect(this, SIGNAL(clientIsEmbedded()),
79
this, SLOT(ensureValidSize()));
83
X11EmbedContainer::~X11EmbedContainer()
85
FdoSelectionManager::manager()->removeDamageWatch(this);
90
void X11EmbedContainer::embedSystemTrayClient(WId clientId)
92
Display *display = QX11Info::display();
94
if (!XGetWindowAttributes(display, clientId, &d->attr)) {
95
emit error(QX11EmbedContainer::Unknown);
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;
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);
109
XWindowAttributes attr;
110
if (!XGetWindowAttributes(display, winId, &attr)) {
111
emit error(QX11EmbedContainer::Unknown);
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())
122
// Redirect ARGB windows to offscreen storage so we can composite them ourselves
123
XRenderPictureAttributes attr;
124
attr.subwindow_mode = IncludeInferiors;
126
d->picture = XRenderCreatePicture(display, clientId, format, CPSubwindowMode, &attr);
127
XCompositeRedirectSubwindows(display, winId, CompositeRedirectManual);
128
FdoSelectionManager::manager()->addDamageWatch(this, clientId);
130
//kDebug() << "Embedded client uses an ARGB visual -> compositing.";
132
//kDebug() << "Embedded client is not using an ARGB visual.";
136
// repeat everything from QX11EmbedContainer's ctor that might be relevant
137
setFocusPolicy(Qt::StrongFocus);
138
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
139
setAcceptDrops(true);
142
XSelectInput(display, winId,
143
KeyPressMask | KeyReleaseMask |
144
ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
147
EnterWindowMask | LeaveWindowMask |
150
StructureNotifyMask |
151
SubstructureNotifyMask);
155
embedClient(clientId);
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);
168
void X11EmbedContainer::ensureValidSize()
170
QSize s = QSize(qBound(minimumSize().width(), width(), maximumSize().width()),
171
qBound(minimumSize().height(), height(), maximumSize().height()));
176
void X11EmbedContainer::setUpdatesEnabled(bool enabled)
178
d->updatesEnabled = enabled;
182
void X11EmbedContainer::paintEvent(QPaintEvent *event)
186
if (!d->updatesEnabled) {
191
FdoSelectionManager::painter()->updateContainer(this);
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();
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)
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);
218
p.drawImage(0, 0, image);
220
XDestroyImage(ximage);
223
pixmap.fill(Qt::transparent);
225
XRenderComposite(x11Info().display(), PictOpSrc, d->picture, None, pixmap.x11PictureHandle(),
226
0, 0, 0, 0, 0, 0, width(), height());
229
p.drawPixmap(0, 0, pixmap);
233
void X11EmbedContainer::setBackgroundPixmap(QPixmap background)
235
if (!clientWinId()) {
239
Display *display = QX11Info::display();
240
Pixmap bg = XCreatePixmap(display, clientWinId(), width(), height(), d->attr.depth);
242
XRenderPictFormat *format = XRenderFindVisualFormat(display, d->attr.visual);
243
Picture picture = XRenderCreatePicture(display, bg, format, 0, 0);
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
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.
250
image = background.copy().toImage(); //With the X11 graphics engine, we have to create a copy first, else we get a crash
252
if(d->oldBackgroundImage == image) {
253
XFreePixmap(display, bg);
254
XRenderFreePicture(display, picture);
257
d->oldBackgroundImage = image;
259
if (background.paintEngine()->type() != QPaintEngine::X11) {
261
XRenderPictFormat *format = 0;
265
if (image.format() == QImage::Format_ARGB32_Premultiplied) {
266
format = XRenderFindStandardFormat(display, PictStandardARGB32);
269
} else if (image.format() == QImage::Format_RGB32) {
270
format = XRenderFindStandardFormat(display, PictStandardRGB24);
273
} else if (image.format() == QImage::Format_RGB16) {
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;
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);
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);
305
image = image.convertToFormat(QImage::Format_RGB32);
306
format = XRenderFindStandardFormat(display, PictStandardRGB24);
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;
320
Q_ASSERT(format != 0);
322
// Get the image data into a pixmap
324
ximage.width = image.width();
325
ximage.height = image.height();
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;
333
ximage.byte_order = LSBFirst;
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;
342
ximage.red_mask = 0x00ff0000;
343
ximage.green_mask = 0x0000ff00;
344
ximage.blue_mask = 0x000000ff;
347
ximage.red_mask = 0xf800;
348
ximage.green_mask = 0x07e0;
349
ximage.blue_mask = 0x001f;
352
if (XInitImage(&ximage) == 0) {
353
XRenderFreePicture(display, picture);
354
XFreePixmap(display, bg);
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);
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);
369
XRenderComposite(display, PictOpSrc, background.x11PictureHandle(),
370
None, picture, 0, 0, 0, 0, 0, 0, width(), height());
373
XSetWindowBackgroundPixmap(display, clientWinId(), bg);
375
XRenderFreePicture(display, picture);
376
XFreePixmap(display, bg);
378
XClearArea(display, clientWinId(), 0, 0, 0, 0, True);
383
#include "x11embedcontainer.moc"