diff options
Diffstat (limited to 'src/widgets/platforms/x11/qx11embed_x11.cpp')
-rw-r--r-- | src/widgets/platforms/x11/qx11embed_x11.cpp | 1810 |
1 files changed, 0 insertions, 1810 deletions
diff --git a/src/widgets/platforms/x11/qx11embed_x11.cpp b/src/widgets/platforms/x11/qx11embed_x11.cpp deleted file mode 100644 index 3213b97629..0000000000 --- a/src/widgets/platforms/x11/qx11embed_x11.cpp +++ /dev/null @@ -1,1810 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qplatformdefs.h" -#include "qx11embed_x11.h" -#include <qapplication.h> -#include <qevent.h> -#include <qpainter.h> -#include <qlayout.h> -#include <qstyle.h> -#include <qstyleoption.h> -#include <qelapsedtimer.h> -#include <qpointer.h> -#include <qdebug.h> -#include <qx11info_x11.h> -#include <private/qt_x11_p.h> -#include <private/qwidget_p.h> - -#define XK_MISCELLANY -#define XK_LATIN1 -#define None 0 -#include <X11/Xlib.h> -#include <X11/Xatom.h> -#include <X11/Xutil.h> -#include <X11/keysymdef.h> -#include <X11/X.h> - -#ifndef XK_ISO_Left_Tab -#define XK_ISO_Left_Tab 0xFE20 -#endif - -//#define QX11EMBED_DEBUG -#ifdef QX11EMBED_DEBUG -#include <qdebug.h> -#endif - -QT_BEGIN_NAMESPACE - -/*! - \class QX11EmbedWidget - \ingroup advanced - \inmodule QtWidgets - - \brief The QX11EmbedWidget class provides an XEmbed client widget. - - XEmbed is an X11 protocol that supports the embedding of a widget - from one application into another application. - - An XEmbed \e{client widget} is a window that is embedded into a - \e container. A container is the graphical location that embeds - (or \e swallows) an external application. - - QX11EmbedWidget is a widget used for writing XEmbed applets or - plugins. When it has been embedded and the container receives tab - focus, focus is passed on to the widget. When the widget reaches - the end of its focus chain, focus is passed back to the - container. Window activation, accelerator support, modality and - drag and drop (XDND) are also handled. - - The widget and container can both initiate the embedding. If the - widget is the initiator, the X11 window ID of the container that - it wants to embed itself into must be passed to embedInto(). - - If the container initiates the embedding, the window ID of the - embedded widget must be known. The container calls embed(), - passing the window ID. - - This example shows an application that embeds a QX11EmbedWidget - subclass into the window whose ID is passed as a command-line - argument: - - \snippet doc/src/snippets/qx11embedwidget/main.cpp 0 - - The problem of obtaining the window IDs is often solved by the - container invoking the application that provides the widget as a - separate process (as a panel invokes a docked applet), passing - its window ID to the new process as a command-line argument. The - new process can then call embedInto() with the container's window - ID, as shown in the example code above. Similarly, the new - process can report its window ID to the container through IPC, in - which case the container can embed the widget. - - When the widget has been embedded, it emits the signal - embedded(). If it is closed by the container, the widget emits - containerClosed(). If an error occurs when embedding, error() is - emitted. - - There are XEmbed widgets available for KDE and GTK+. The GTK+ - equivalent of QX11EmbedWidget is GtkPlug. The corresponding KDE 3 - widget is called QXEmbed. - - \sa QX11EmbedContainer, {XEmbed Specification} -*/ - -/*! - \class QX11EmbedContainer - \ingroup advanced - \inmodule QtWidgets - - \brief The QX11EmbedContainer class provides an XEmbed container - widget. - - XEmbed is an X11 protocol that supports the embedding of a widget - from one application into another application. - - An XEmbed \e container is the graphical location that embeds an - external \e {client widget}. A client widget is a window that is - embedded into a container. - - When a widget has been embedded and the container receives tab - focus, focus is passed on to the widget. When the widget reaches - the end of its focus chain, focus is passed back to the - container. Window activation, accelerator support, modality and - drag and drop (XDND) are also handled. - - QX11EmbedContainer is commonly used for writing panels or - toolbars that hold applets, or for \e swallowing X11 - applications. When writing a panel application, one container - widget is created on the toolbar, and it can then either swallow - another widget using embed(), or allow an XEmbed widget to be - embedded into itself. The container's X11 window ID, which is - retrieved with winId(), must then be known to the client widget. - After embedding, the client's window ID can be retrieved with - clientWinId(). - - In the following example, a container widget is created as the - main widget. It then invokes an application called "playmovie", - passing its window ID as a command line argument. The "playmovie" - program is an XEmbed client widget. The widget embeds itself into - the container using the container's window ID. - - \snippet doc/src/snippets/qx11embedcontainer/main.cpp 0 - - When the client widget is embedded, the container emits the - signal clientIsEmbedded(). The signal clientClosed() is emitted - when a widget is closed. - - It is possible for QX11EmbedContainer to embed XEmbed widgets - from toolkits other than Qt, such as GTK+. Arbitrary (non-XEmbed) - X11 widgets can also be embedded, but the XEmbed-specific - features such as window activation and focus handling are then - lost. - - The GTK+ equivalent of QX11EmbedContainer is GtkSocket. The - corresponding KDE 3 widget is called QXEmbed. - - \sa QX11EmbedWidget, {XEmbed Specification} -*/ - -/*! \fn void QX11EmbedWidget::embedded() - - This signal is emitted by the widget that has been embedded by an - XEmbed container. -*/ - -/*! \fn void QX11EmbedWidget::containerClosed() - - This signal is emitted by the client widget when the container - closes the widget. This can happen if the container itself - closes, or if the widget is rejected. - - The container can reject a widget for any reason, but the most - common cause of a rejection is when an attempt is made to embed a - widget into a container that already has an embedded widget. -*/ - -/*! \fn void QX11EmbedContainer::clientIsEmbedded() - - This signal is emitted by the container when a client widget has - been embedded. -*/ - -/*! \fn void QX11EmbedContainer::clientClosed() - - This signal is emitted by the container when the client widget - closes. -*/ - -/*! - \fn void QX11EmbedWidget::error(QX11EmbedWidget::Error error) - - This signal is emitted if an error occurred as a result of - embedding into or communicating with a container. The specified - \a error describes the problem that occurred. - - \sa QX11EmbedWidget::Error -*/ - -/*! - \fn QX11EmbedContainer::Error QX11EmbedContainer::error() const - - Returns the last error that occurred. -*/ - -/*! \fn void QX11EmbedContainer::error(QX11EmbedContainer::Error error) - - This signal is emitted if an error occurred when embedding or - communicating with a client. The specified \a error describes the - problem that occurred. - - \sa QX11EmbedContainer::Error -*/ - -/*! - \enum QX11EmbedWidget::Error - - \value Unknown An unrecognized error occurred. - - \value InvalidWindowID The X11 window ID of the container was - invalid. This error is usually triggered by passing an invalid - window ID to embedInto(). - - \omitvalue Internal -*/ - -/*! - \enum QX11EmbedContainer::Error - - \value Unknown An unrecognized error occurred. - - \value InvalidWindowID The X11 window ID of the container was - invalid. This error is usually triggered by passing an invalid - window ID to embed(). - - \omitvalue Internal -*/ - -const int XButtonPress = ButtonPress; -const int XButtonRelease = ButtonRelease; -#undef ButtonPress -#undef ButtonRelease - -// This is a hack to move topData() out from QWidgetPrivate to public. We -// need to to inspect window()'s embedded state. -class QHackWidget : public QWidget -{ - Q_DECLARE_PRIVATE(QWidget) -public: - QTLWExtra* topData() { return d_func()->topData(); } -}; - -static unsigned int XEMBED_VERSION = 0; - -enum QX11EmbedMessageType { - XEMBED_EMBEDDED_NOTIFY = 0, - XEMBED_WINDOW_ACTIVATE = 1, - XEMBED_WINDOW_DEACTIVATE = 2, - XEMBED_REQUEST_FOCUS = 3, - XEMBED_FOCUS_IN = 4, - XEMBED_FOCUS_OUT = 5, - XEMBED_FOCUS_NEXT = 6, - XEMBED_FOCUS_PREV = 7, - XEMBED_MODALITY_ON = 10, - XEMBED_MODALITY_OFF = 11, - XEMBED_REGISTER_ACCELERATOR = 12, - XEMBED_UNREGISTER_ACCELERATOR = 13, - XEMBED_ACTIVATE_ACCELERATOR = 14 -}; - -enum QX11EmbedFocusInDetail { - XEMBED_FOCUS_CURRENT = 0, - XEMBED_FOCUS_FIRST = 1, - XEMBED_FOCUS_LAST = 2 -}; - -enum QX11EmbedFocusInFlags { - XEMBED_FOCUS_OTHER = (0 << 0), - XEMBED_FOCUS_WRAPAROUND = (1 << 0) -}; - -enum QX11EmbedInfoFlags { - XEMBED_MAPPED = (1 << 0) -}; - -enum QX11EmbedAccelModifiers { - XEMBED_MODIFIER_SHIFT = (1 << 0), - XEMBED_MODIFIER_CONTROL = (1 << 1), - XEMBED_MODIFIER_ALT = (1 << 2), - XEMBED_MODIFIER_SUPER = (1 << 3), - XEMBED_MODIFIER_HYPER = (1 << 4) -}; - -enum QX11EmbedAccelFlags { - XEMBED_ACCELERATOR_OVERLOADED = (1 << 0) -}; - -// Silence the default X11 error handler. -static int x11ErrorHandler(Display *, XErrorEvent *) -{ - return 0; -} - -// Returns the X11 timestamp. Maintained mainly by qapplication -// internals, but also updated by the XEmbed widgets. -static Time x11Time() -{ - return qt_x11Data->time; -} - -// Gives the version and flags of the supported XEmbed protocol. -static unsigned int XEmbedVersion() -{ - return 0; -} - -// Sends an XEmbed message. -static void sendXEmbedMessage(WId window, Display *display, long message, - long detail = 0, long data1 = 0, long data2 = 0) -{ - XClientMessageEvent c; - memset(&c, 0, sizeof(c)); - c.type = ClientMessage; - c.message_type = ATOM(_XEMBED); - c.format = 32; - c.display = display; - c.window = window; - - c.data.l[0] = x11Time(); - c.data.l[1] = message; - c.data.l[2] = detail; - c.data.l[3] = data1; - c.data.l[4] = data2; - - XSendEvent(display, window, false, NoEventMask, (XEvent *) &c); -} - -// From qapplication_x11.cpp -static XKeyEvent lastKeyEvent; - -static QCoreApplication::EventFilter oldX11EventFilter; - -// The purpose of this global x11 filter is for one to capture the key -// events in their original state, but most importantly this is the -// only way to get the WM_TAKE_FOCUS message from WM_PROTOCOLS. -static bool x11EventFilter(void *message, long *result) -{ - XEvent *event = reinterpret_cast<XEvent *>(message); - if (event->type == XKeyPress || event->type == XKeyRelease) - lastKeyEvent = event->xkey; - - if (oldX11EventFilter && oldX11EventFilter != &x11EventFilter) - return oldX11EventFilter(message, result); - else - return false; -} - -// -struct functorData -{ - Window id, rootWindow; - bool clearedWmState; - bool reparentedToRoot; -}; - -static Bool functor(Display *display, XEvent *event, XPointer arg) -{ - functorData *data = (functorData *) arg; - - if (!data->reparentedToRoot && event->type == ReparentNotify - && event->xreparent.window == data->id - && event->xreparent.parent == data->rootWindow) { - data->reparentedToRoot = true; - return true; - } - - if (!data->clearedWmState - && event->type == PropertyNotify - && event->xproperty.window == data->id - && event->xproperty.atom == ATOM(WM_STATE)) { - if (event->xproperty.state == PropertyDelete) { - data->clearedWmState = true; - return true; - } - - Atom ret; - int format, status; - unsigned char *retval; - unsigned long nitems, after; - status = XGetWindowProperty(display, data->id, ATOM(WM_STATE), 0, 2, False, ATOM(WM_STATE), - &ret, &format, &nitems, &after, &retval ); - if (status == Success && ret == ATOM(WM_STATE) && format == 32 && nitems > 0) { - long state = *(long *)retval; - XFree(retval); - if (state == WithdrawnState) { - data->clearedWmState = true; - return true; - } - } - } - - return false; -} - -class QX11EmbedWidgetPrivate : public QWidgetPrivate -{ - Q_DECLARE_PUBLIC(QX11EmbedWidget) -public: - inline QX11EmbedWidgetPrivate() - { - lastError = QX11EmbedWidget::Unknown; - container = 0; - } - - void setEmbedded(); - - void emitError(QX11EmbedWidget::Error error) { - Q_Q(QX11EmbedWidget); - - lastError = error; - emit q->error(error); - } - - enum FocusWidgets { - FirstFocusWidget, - LastFocusWidget - }; - - int focusOriginator; - QWidget *getFocusWidget(FocusWidgets fw); - void checkActivateWindow(QObject *o); - QX11EmbedWidget *xEmbedWidget(QObject *o) const; - void clearFocus(); - - WId container; - QPointer<QWidget> currentFocus; - - QX11EmbedWidget::Error lastError; - -}; - -/*! - Constructs a QX11EmbedWidget object with the given \a parent. -*/ -QX11EmbedWidget::QX11EmbedWidget(QWidget *parent) - : QWidget(*new QX11EmbedWidgetPrivate, parent, 0) -{ - XSetErrorHandler(x11ErrorHandler); - - setAttribute(Qt::WA_NativeWindow); - setAttribute(Qt::WA_DontCreateNativeAncestors); - createWinId(); - XSelectInput(x11Info().display(), internalWinId(), - KeyPressMask | KeyReleaseMask | ButtonPressMask - | ButtonReleaseMask - | KeymapStateMask | ButtonMotionMask | PointerMotionMask - | FocusChangeMask - | ExposureMask | StructureNotifyMask - | SubstructureNotifyMask | PropertyChangeMask); - - long data[] = {XEMBED_VERSION, XEMBED_MAPPED}; - XChangeProperty(x11Info().display(), internalWinId(), ATOM(_XEMBED_INFO), - ATOM(_XEMBED_INFO), 32, PropModeReplace, - (unsigned char*) data, 2); - - setFocusPolicy(Qt::StrongFocus); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - QApplication::instance()->installEventFilter(this); - -#ifdef QX11EMBED_DEBUG - qDebug() << "QX11EmbedWidget::QX11EmbedWidget: constructed client" - << (void *)this << "with winId" << winId(); -#endif -} - -/*! - Destructs the QX11EmbedWidget object. If the widget is embedded - when deleted, it is hidden and then detached from its container, - so that the container is free to embed a new widget. -*/ -QX11EmbedWidget::~QX11EmbedWidget() -{ - Q_D(QX11EmbedWidget); - if (d->container) { -#ifdef QX11EMBED_DEBUG - qDebug() << "QX11EmbedWidget::~QX11EmbedWidget: unmapping" - << (void *)this << "with winId" << winId() - << "from container with winId" << d->container; -#endif - XUnmapWindow(x11Info().display(), internalWinId()); - XReparentWindow(x11Info().display(), internalWinId(), x11Info().appRootWindow(x11Info().screen()), 0, 0); - } - -#ifdef QX11EMBED_DEBUG - qDebug() << "QX11EmbedWidget::~QX11EmbedWidget: destructed client" - << (void *)this << "with winId" << winId(); -#endif -} - -/*! - Returns the type of error that occurred last. This is the same error code - that is emitted by the error() signal. - - \sa Error -*/ -QX11EmbedWidget::Error QX11EmbedWidget::error() const -{ - return d_func()->lastError; -} - -/*! - When this function is called, the widget embeds itself into the - container whose window ID is \a id. - - If \a id is \e not the window ID of a container this function will - behave unpredictably. -*/ -void QX11EmbedWidget::embedInto(WId id) -{ - Q_D(QX11EmbedWidget); -#ifdef QX11EMBED_DEBUG - qDebug() << "QX11EmbedWidget::embedInto: embedding client" - << (void *)this << "with winId" << winId() << "into container" - << id; -#endif - - d->container = id; - switch (XReparentWindow(x11Info().display(), internalWinId(), d->container, 0, 0)) { - case BadWindow: - d->emitError(InvalidWindowID); - break; - case BadMatch: - d->emitError(Internal); - break; - case Success: - default: - break; - } - QTLWExtra* x = d->extra ? d->extra->topextra : 0; - if (x) - x->frameStrut.setCoords(0, 0, 0, 0); - d->data.fstrut_dirty = false; -} - -/*! \internal - - Gets the first or last child widget that can get focus. -*/ -QWidget *QX11EmbedWidgetPrivate::getFocusWidget(FocusWidgets fw) -{ - Q_Q(QX11EmbedWidget); - QWidget *tlw = q; - QWidget *w = tlw->nextInFocusChain(); - - QWidget *last = tlw; - - extern bool qt_tab_all_widgets; - uint focus_flag = qt_tab_all_widgets ? Qt::TabFocus : Qt::StrongFocus; - - while (w != tlw) - { - if (((w->focusPolicy() & focus_flag) == focus_flag) - && w->isVisibleTo(q) && w->isEnabled()) - { - last = w; - if (fw == FirstFocusWidget) - break; - } - w = w->nextInFocusChain(); - } - - return last; -} - -/*! \internal - - Returns the xembed widget that the widget is a child of -*/ -QX11EmbedWidget *QX11EmbedWidgetPrivate::xEmbedWidget(QObject *o) const -{ - QX11EmbedWidget *xec = 0; - - // Check the widget itself, then its parents, and find the first - // QX11EmbedWidget. - do { - if ((xec = qobject_cast<QX11EmbedWidget *>(o))) - return xec; - } while ((o = o->parent())); - return 0; -} - -/*! \internal - - Checks the active window. -*/ -void QX11EmbedWidgetPrivate::checkActivateWindow(QObject *o) -{ - Q_Q(QX11EmbedWidget); - QX11EmbedWidget *xec = xEmbedWidget(o); - - // check if we are in the right xembed client - if (q != xec) - return; - - QWidget *w = qobject_cast<QWidget *>(o); - - // if it is no active window, then don't do the change - if (!(w && qApp->activeWindow())) - return; - - // if it already is the active window, don't do anything - if (w->window() != qApp->activeWindow()) - { - qApp->setActiveWindow(w->window()); - currentFocus = w; - - sendXEmbedMessage(xec->containerWinId(), q->x11Info().display(), XEMBED_REQUEST_FOCUS); - } -} - -/*! \internal - - Clears the focus -*/ -void QX11EmbedWidgetPrivate::clearFocus() -{ - Q_Q(QX11EmbedWidget); - // Setting focus on the client itself removes Qt's - // logical focus rectangle. We can't just do a - // clearFocus here, because when we send the synthetic - // FocusIn to ourselves on activation, Qt will set - // focus on focusWidget() again. This way, we "hide" - // focus rather than clearing it. - - if (!q->window()->hasFocus()) - q->window()->setFocus(Qt::OtherFocusReason); - - currentFocus = 0; -} - -/*! \internal - - Sets the embedded flag on the client. -*/ -void QX11EmbedWidgetPrivate::setEmbedded() -{ - Q_Q(QX11EmbedWidget); - ((QHackWidget *)q->window())->topData()->embedded = 1; -} - -/*! \internal - - Handles WindowActivate and FocusIn events for the client. -*/ -bool QX11EmbedWidget::eventFilter(QObject *o, QEvent *event) -{ - Q_D(QX11EmbedWidget); - switch (event->type()) { - case QEvent::FocusIn: - switch (((QFocusEvent *)event)->reason()) { - case Qt::MouseFocusReason: - // If the user clicks into one of the client widget's - // children and we didn't have focus already, we request - // focus from our container. - if (d->xEmbedWidget(o) == this) { - if (d->currentFocus.isNull()) - sendXEmbedMessage(d->container, x11Info().display(), XEMBED_REQUEST_FOCUS); - - d->currentFocus = qobject_cast<QWidget *>(o); - } - break; - case Qt::TabFocusReason: - // If the xembed client receives a focus event because of - // a Tab, then we are at the end of our focus chain and we - // ask the container to move to its next focus widget. - if (o == this) { - d->clearFocus(); - sendXEmbedMessage(d->container, x11Info().display(), XEMBED_FOCUS_NEXT); - return true; - } else { - // We're listening on events from qApp, so in order - // for us to know who to set focus on if we receive an - // activation event, we note the widget that got the - // focusin last. - if (d->xEmbedWidget(o) == this) - d->currentFocus = qobject_cast<QWidget *>(o); - } - break; - case Qt::BacktabFocusReason: - // If the window receives a focus event because of - // a Backtab, then we are at the start of our focus chain - // and we ask the container to move to its previous focus - // widget. - if (o == this) { - // See comment for Tab. - // If we receive a XEMBED_FOCUS_IN - // XEMBED_FOCUS_CURRENT, we will set focus in - // currentFocus. To avoid that in this case, we reset - // currentFocus. - d->clearFocus(); - sendXEmbedMessage(d->container, x11Info().display(), XEMBED_FOCUS_PREV); - return true; - } else { - if (d->xEmbedWidget(o) == this) - d->currentFocus = qobject_cast<QWidget *>(o); - } - break; - case Qt::ActiveWindowFocusReason: - if (isActiveWindow()) { - if (!d->currentFocus.isNull()) { - if (!d->currentFocus->hasFocus()) - d->currentFocus->setFocus(Qt::OtherFocusReason); - } else { - d->clearFocus(); - return true; - } - } - - break; - case Qt::PopupFocusReason: - case Qt::ShortcutFocusReason: - case Qt::OtherFocusReason: - // If focus is received to any child widget because of any - // other reason, remember the widget so that we can give - // it focus again if we're activated. - if (d->xEmbedWidget(o) == this) { - d->currentFocus = qobject_cast<QWidget *>(o); - } - break; - default: - break; - } - break; - case QEvent::MouseButtonPress: - // If we get a mouse button press event inside a embedded widget - // make sure this is the active window in qapp. - d->checkActivateWindow(o); - break; - default: - break; - } - - return QWidget::eventFilter(o, event); -} - -/*! \internal - - Handles some notification events and client messages. Client side - XEmbed message receiving is also handled here. -*/ -bool QX11EmbedWidget::x11Event(XEvent *event) -{ - Q_D(QX11EmbedWidget); - switch (event->type) { - case DestroyNotify: -#ifdef QX11EMBED_DEBUG - qDebug() << "QX11EmbedWidget::x11Event: client" - << (void *)this << "with winId" << winId() - << "received a DestroyNotify"; -#endif - // If the container window is destroyed, we signal this to the user. - d->container = 0; - emit containerClosed(); - break; - case ReparentNotify: -#ifdef QX11EMBED_DEBUG - qDebug() << "QX11EmbedWidget::x11Event: client" - << (void *)this << "with winId" << winId() - << "received a ReparentNotify to" - << ((event->xreparent.parent == x11Info().appRootWindow(x11Info().screen())) - ? QString::fromLatin1("root") : QString::number(event->xreparent.parent)); -#endif - // If the container shuts down, we will be reparented to the - // root window. We must also consider the case that we may be - // reparented from one container to another. - if (event->xreparent.parent == x11Info().appRootWindow(x11Info().screen())) { - if (((QHackWidget *)this)->topData()->embedded) { - d->container = 0; - emit containerClosed(); - } - return true; - } else { - d->container = event->xreparent.parent; - } - break; - case UnmapNotify: - // Mapping and unmapping are handled by changes to the - // _XEMBED_INFO property. Any default map/unmap requests are - // ignored. - return true; - case PropertyNotify: - // The container sends us map/unmap messages through the - // _XEMBED_INFO property. We adhere to the XEMBED_MAPPED bit in - // data2. - if (event->xproperty.atom == ATOM(_XEMBED_INFO)) { - Atom actual_type_return; - int actual_format_return; - unsigned long nitems_return; - unsigned long bytes_after_return; - unsigned char *prop_return = 0; - if (XGetWindowProperty(x11Info().display(), internalWinId(), ATOM(_XEMBED_INFO), 0, 2, - false, ATOM(_XEMBED_INFO), &actual_type_return, - &actual_format_return, &nitems_return, - &bytes_after_return, &prop_return) == Success) { - if (nitems_return > 1) { - if (((long * )prop_return)[1] & XEMBED_MAPPED) { - XMapWindow(x11Info().display(), internalWinId()); - } else { - XUnmapWindow(x11Info().display(), internalWinId()); - } - } - if (prop_return) - XFree(prop_return); - } - } - - break; - case ClientMessage: - // XEMBED messages have message_type _XEMBED - if (event->xclient.message_type == ATOM(_XEMBED)) { - // Discard XEMBED messages not to ourselves. (### dead code?) - if (event->xclient.window != internalWinId()) - break; - - // Update qt_x_time if necessary - Time msgtime = (Time) event->xclient.data.l[0]; - if (msgtime > X11->time) - X11->time = msgtime; - - switch (event->xclient.data.l[1]) { - case XEMBED_WINDOW_ACTIVATE: { - // When we receive an XEMBED_WINDOW_ACTIVATE, we simply send - // ourselves a WindowActivate event. Real activation happens - // when receive XEMBED_FOCUS_IN. - if (!isActiveWindow()) { - QEvent ev(QEvent::WindowActivate); - QApplication::sendEvent(this, &ev); - } - } - break; - case XEMBED_WINDOW_DEACTIVATE: { - // When we receive an XEMBED_WINDOW_DEACTIVATE, we simply send - // ourselves a WindowDeactivate event. Real activation happens - // when receive XEMBED_FOCUS_IN. - if (isActiveWindow()) { - if (!qApp->activePopupWidget()) - QApplication::setActiveWindow(0); - } else { - QEvent ev(QEvent::WindowDeactivate); - QApplication::sendEvent(this, &ev); - } - } - break; - case XEMBED_EMBEDDED_NOTIFY: { -#ifdef QX11EMBED_DEBUG - qDebug() << "QX11EmbedWidget::x11Event: client" - << (void *)this << "with winId" << winId() - << "received an XEMBED EMBEDDED NOTIFY message"; -#endif - // In this message's l[2] we have the max version - // supported by both the client and the - // container. QX11EmbedWidget does not use this field. - - // We have been embedded, so we set our - // client's embedded flag. - d->setEmbedded(); - emit embedded(); - } - break; - case XEMBED_FOCUS_IN: - // don't set the focus if a modal dialog is open - if (qApp->activeModalWidget()) - break; - - // in case we embed more than one topLevel window inside the same - // host window. - if (window() != qApp->activeWindow()) - qApp->setActiveWindow(this); - - switch (event->xclient.data.l[2]) { - case XEMBED_FOCUS_CURRENT: - // The container sends us this message if it wants - // us to focus on the widget that last had focus. - // This is the reply when XEMBED_REQUEST_FOCUS is - // sent to the container. - if (!d->currentFocus.isNull()) { - if (!d->currentFocus->hasFocus()) - d->currentFocus->setFocus(Qt::OtherFocusReason); - } else { - // No widget currently has focus. We set focus - // on the first widget next to the - // client widget. Since the setFocus will not work - // if the window is disabled, set the currentFocus - // directly so that it's set on window activate. - d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::FirstFocusWidget); - d->currentFocus->setFocus(Qt::OtherFocusReason); - } - break; - case XEMBED_FOCUS_FIRST: - // The container sends this message when it wants - // us to focus on the first widget in our focus - // chain (typically because of a tab). - d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::FirstFocusWidget); - d->currentFocus->setFocus(Qt::TabFocusReason); - break; - case XEMBED_FOCUS_LAST: - // The container sends this message when it wants - // us to focus on the last widget in our focus - // chain (typically because of a backtab). - d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::LastFocusWidget); - d->currentFocus->setFocus(Qt::BacktabFocusReason); - break; - default: - // Ignore any other XEMBED_FOCUS_IN details. - break; - } - break; - case XEMBED_FOCUS_OUT: - // The container sends us this message when it wants us - // to lose focus and forget about the widget that last - // had focus. Typically sent by the container when it - // loses focus because of mouse or tab activity. We do - // then not want to set focus on anything if we're - // activated. - if (isActiveWindow()) - d->clearFocus(); - - break; - default: - // Ignore any other XEMBED messages. - break; - }; - } else { - // Non-XEMBED client messages are not interesting. - } - - break; - default: - // Ignore all other x11 events. - break; - } - - // Allow default handling. - return QWidget::x11Event(event); -} - -/*! - \reimp -*/ -bool QX11EmbedWidget::event(QEvent *event) -{ - if (event->type() == QEvent::ParentChange) { - XSelectInput(x11Info().display(), internalWinId(), - KeyPressMask | KeyReleaseMask | ButtonPressMask - | ButtonReleaseMask - | KeymapStateMask | ButtonMotionMask | PointerMotionMask - | FocusChangeMask - | ExposureMask | StructureNotifyMask - | SubstructureNotifyMask | PropertyChangeMask); - } - return QWidget::event(event); -} - -/*! - \reimp -*/ -void QX11EmbedWidget::resizeEvent(QResizeEvent *event) -{ - if (layout()) - layout()->update(); - QWidget::resizeEvent(event); -} - -/*! - If the widget is embedded, returns the window ID of the - container; otherwize returns 0. -*/ -WId QX11EmbedWidget::containerWinId() const -{ - Q_D(const QX11EmbedWidget); - return d->container; -} - -class QX11EmbedContainerPrivate : public QWidgetPrivate -{ - Q_DECLARE_PUBLIC(QX11EmbedContainer) -public: - inline QX11EmbedContainerPrivate() - { - lastError = QX11EmbedContainer::Unknown; - client = 0; - focusProxy = 0; - clientIsXEmbed = false; - xgrab = false; - } - - bool isEmbedded() const; - void moveInputToProxy(); - - void acceptClient(WId window); - void rejectClient(WId window); - - void checkGrab(); - - WId topLevelParentWinId() const; - - void emitError(QX11EmbedContainer::Error error) { - Q_Q(QX11EmbedContainer); - lastError = error; - emit q->error(error); - } - - WId client; - QWidget *focusProxy; - bool clientIsXEmbed; - bool xgrab; - QRect clientOriginalRect; - QSize wmMinimumSizeHint; - - QX11EmbedContainer::Error lastError; - - static QX11EmbedContainer *activeContainer; -}; - -QX11EmbedContainer *QX11EmbedContainerPrivate::activeContainer = 0; - -/*! - Creates a QX11EmbedContainer object with the given \a parent. -*/ -QX11EmbedContainer::QX11EmbedContainer(QWidget *parent) - : QWidget(*new QX11EmbedContainerPrivate, parent, 0) -{ - Q_D(QX11EmbedContainer); - XSetErrorHandler(x11ErrorHandler); - - setAttribute(Qt::WA_NativeWindow); - setAttribute(Qt::WA_DontCreateNativeAncestors); - createWinId(); - - setFocusPolicy(Qt::StrongFocus); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - // ### PORT setKeyCompression(false); - setAcceptDrops(true); - setEnabled(false); - - // Everybody gets a focus proxy, but only one toplevel container's - // focus proxy is actually in use. - d->focusProxy = new QWidget(this); - d->focusProxy->setAttribute(Qt::WA_NativeWindow); - d->focusProxy->setAttribute(Qt::WA_DontCreateNativeAncestors); - d->focusProxy->createWinId(); - d->focusProxy->setGeometry(-1, -1, 1, 1); - - // We need events from the window (activation status) and - // from qApp (keypress/release). - qApp->installEventFilter(this); - - // Install X11 event filter. - if (!oldX11EventFilter) - oldX11EventFilter = QCoreApplication::instance()->setEventFilter(x11EventFilter); - - XSelectInput(x11Info().display(), internalWinId(), - KeyPressMask | KeyReleaseMask - | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask - | KeymapStateMask - | PointerMotionMask - | EnterWindowMask | LeaveWindowMask - | FocusChangeMask - | ExposureMask - | StructureNotifyMask - | SubstructureNotifyMask); - - // Make sure our new event mask takes effect as soon as possible. - XFlush(x11Info().display()); - - // Move input to our focusProxy if this widget is active, and not - // shaded by a modal dialog (in which case isActiveWindow() would - // still return true, but where we must not move input focus). - if (qApp->activeWindow() == window() && !d->isEmbedded()) - d->moveInputToProxy(); - -#ifdef QX11EMBED_DEBUG - qDebug() << "QX11EmbedContainer::QX11EmbedContainer: constructed container" - << (void *)this << "with winId" << winId(); -#endif -} - -/*! - Destructs a QX11EmbedContainer. -*/ -QX11EmbedContainer::~QX11EmbedContainer() -{ - Q_D(QX11EmbedContainer); - if (d->client) { - XUnmapWindow(x11Info().display(), d->client); - XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(x11Info().screen()), 0, 0); - } - - if (d->xgrab) - XUngrabButton(x11Info().display(), AnyButton, AnyModifier, internalWinId()); -} - - -QX11EmbedContainer::Error QX11EmbedContainer::error() const { - return d_func()->lastError; -} - - -/*! \reimp -*/ -void QX11EmbedContainer::paintEvent(QPaintEvent *) -{ -} - -/*! \internal - - Returns whether or not the windows' embedded flag is set. -*/ -bool QX11EmbedContainerPrivate::isEmbedded() const -{ - Q_Q(const QX11EmbedContainer); - return ((QHackWidget *)q->window())->topData()->embedded == 1; -} - -/*! \internal - - Returns the parentWinId of the window. -*/ -WId QX11EmbedContainerPrivate::topLevelParentWinId() const -{ - Q_Q(const QX11EmbedContainer); - return ((QHackWidget *)q->window())->topData()->parentWinId; -} - -/*! - If the container has an embedded widget, this function returns - the X11 window ID of the client; otherwise it returns 0. -*/ -WId QX11EmbedContainer::clientWinId() const -{ - Q_D(const QX11EmbedContainer); - return d->client; -} - -/*! - Instructs the container to embed the X11 window with window ID \a - id. The client widget will then move on top of the container - window and be resized to fit into the container. - - The \a id should be the ID of a window controlled by an XEmbed - enabled application, but this is not mandatory. If \a id does not - belong to an XEmbed client widget, then focus handling, - activation, accelerators and other features will not work - properly. -*/ -void QX11EmbedContainer::embedClient(WId id) -{ - Q_D(QX11EmbedContainer); - - if (id == 0) { - d->emitError(InvalidWindowID); - return; - } - - // Walk up the tree of parent windows to prevent embedding of ancestors. - WId thisId = internalWinId(); - Window rootReturn; - Window parentReturn; - Window *childrenReturn = 0; - unsigned int nchildrenReturn; - do { - if (XQueryTree(x11Info().display(), thisId, &rootReturn, - &parentReturn, &childrenReturn, &nchildrenReturn) == 0) { - d->emitError(InvalidWindowID); - return; - } - if (childrenReturn) { - XFree(childrenReturn); - childrenReturn = 0; - } - - thisId = parentReturn; - if (id == thisId) { - d->emitError(InvalidWindowID); - return; - } - } while (thisId != rootReturn); - - // watch for property notify events (see below) - XGrabServer(x11Info().display()); - XWindowAttributes attrib; - if (!XGetWindowAttributes(x11Info().display(), id, &attrib)) { - XUngrabServer(x11Info().display()); - d->emitError(InvalidWindowID); - return; - } - XSelectInput(x11Info().display(), id, attrib.your_event_mask | PropertyChangeMask | StructureNotifyMask); - XUngrabServer(x11Info().display()); - - // Put the window into WithdrawnState - XUnmapWindow(x11Info().display(), id); - XSync(x11Info().display(), False); // make sure the window is hidden - - /* - Wait for notification from the window manager that the window is - in withdrawn state. According to the ICCCM section 4.1.3.1, - we should wait for the WM_STATE property to either be deleted or - set to WithdrawnState. - - For safety, we will not wait more than 500 ms, so that we can - preemptively workaround buggy window managers. - */ - QElapsedTimer t; - t.start(); - - functorData data; - data.id = id; - data.rootWindow = attrib.root; - data.clearedWmState = false; - data.reparentedToRoot = false; - - do { - if (t.elapsed() > 500) // time-out after 500 ms - break; - - XEvent event; - if (!XCheckIfEvent(x11Info().display(), &event, functor, (XPointer) &data)) { - XSync(x11Info().display(), False); - usleep(50000); - continue; - } - - qApp->x11ProcessEvent(&event); - } while (!data.clearedWmState || !data.reparentedToRoot); - - // restore the event mask - XSelectInput(x11Info().display(), id, attrib.your_event_mask); - - switch (XReparentWindow(x11Info().display(), id, internalWinId(), 0, 0)) { - case BadWindow: - case BadMatch: - d->emitError(InvalidWindowID); - break; - default: - break; - } -} - -/*! \internal - - Handles key, activation and focus events for the container. -*/ -bool QX11EmbedContainer::eventFilter(QObject *o, QEvent *event) -{ - Q_D(QX11EmbedContainer); - switch (event->type()) { - case QEvent::KeyPress: - // Forward any keypresses to our client. - if (o == this && d->client) { - lastKeyEvent.window = d->client; - XSendEvent(x11Info().display(), d->client, false, KeyPressMask, (XEvent *) &lastKeyEvent); - return true; - } - break; - case QEvent::KeyRelease: - // Forward any keyreleases to our client. - if (o == this && d->client) { - lastKeyEvent.window = d->client; - XSendEvent(x11Info().display(), d->client, false, KeyReleaseMask, (XEvent *) &lastKeyEvent); - return true; - } - break; - - case QEvent::WindowActivate: - // When our container window is activated, we pass the - // activation message on to our client. Note that X input - // focus is set to our focus proxy. We want to intercept all - // keypresses. - if (o == window() && d->client) { - if (d->clientIsXEmbed) { - sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_ACTIVATE); - } else { - d->checkGrab(); - if (hasFocus()) - XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time()); - } - if (!d->isEmbedded()) - d->moveInputToProxy(); - } - break; - case QEvent::WindowDeactivate: - // When our container window is deactivated, we pass the - // deactivation message to our client. - if (o == window() && d->client) { - if (d->clientIsXEmbed) - sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_DEACTIVATE); - else - d->checkGrab(); - } - break; - case QEvent::FocusIn: - // When receiving FocusIn events generated by Tab or Backtab, - // we pass focus on to our client. Any mouse activity is sent - // directly to the client, and it will ask us for focus with - // XEMBED_REQUEST_FOCUS. - if (o == this && d->client) { - if (!d->isEmbedded()) - d->activeContainer = this; - - if (d->clientIsXEmbed) { - if (!d->isEmbedded()) - d->moveInputToProxy(); - - QFocusEvent *fe = (QFocusEvent *)event; - switch (fe->reason()) { - case Qt::TabFocusReason: - sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST); - break; - case Qt::BacktabFocusReason: - sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_LAST); - break; - default: - sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT); - break; - } - } else { - d->checkGrab(); - XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time()); - } - } - - break; - case QEvent::FocusOut: { - // When receiving a FocusOut, we ask our client to remove its - // focus. - if (o == this && d->client) { - if (!d->isEmbedded()) { - d->activeContainer = 0; - if (isActiveWindow()) - d->moveInputToProxy(); - } - - if (d->clientIsXEmbed) { - QFocusEvent *fe = (QFocusEvent *)event; - if (o == this && d->client && fe->reason() != Qt::ActiveWindowFocusReason) - sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_OUT); - } else { - d->checkGrab(); - } - } - } - break; - - case QEvent::Close: { - if (o == this && d->client) { - // Unmap the client and reparent it to the root window. - // Wait until the messages have been processed. Then ask - // the window manager to delete the window. - XUnmapWindow(x11Info().display(), d->client); - XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(x11Info().screen()), 0, 0); - XSync(x11Info().display(), false); - - XEvent ev; - memset(&ev, 0, sizeof(ev)); - ev.xclient.type = ClientMessage; - ev.xclient.window = d->client; - ev.xclient.message_type = ATOM(WM_PROTOCOLS); - ev.xclient.format = 32; - ev.xclient.data.s[0] = ATOM(WM_DELETE_WINDOW); - XSendEvent(x11Info().display(), d->client, false, NoEventMask, &ev); - - XFlush(x11Info().display()); - d->client = 0; - d->clientIsXEmbed = false; - d->wmMinimumSizeHint = QSize(); - updateGeometry(); - setEnabled(false); - update(); - - emit clientClosed(); - } - } - default: - break; - } - - return QWidget::eventFilter(o, event); -} - -/*! \internal - - Handles X11 events for the container. -*/ -bool QX11EmbedContainer::x11Event(XEvent *event) -{ - Q_D(QX11EmbedContainer); - - switch (event->type) { - case CreateNotify: - // The client created an embedded window. - if (d->client) - d->rejectClient(event->xcreatewindow.window); - else - d->acceptClient(event->xcreatewindow.window); - break; - case DestroyNotify: - if (event->xdestroywindow.window == d->client) { - // The client died. - d->client = 0; - d->clientIsXEmbed = false; - d->wmMinimumSizeHint = QSize(); - updateGeometry(); - update(); - setEnabled(false); - emit clientClosed(); - } - break; - case ReparentNotify: - // The client sends us this if it reparents itself out of our - // widget. - if (event->xreparent.window == d->client && event->xreparent.parent != internalWinId()) { - d->client = 0; - d->clientIsXEmbed = false; - d->wmMinimumSizeHint = QSize(); - updateGeometry(); - update(); - setEnabled(false); - emit clientClosed(); - } else if (event->xreparent.parent == internalWinId()) { - // The client reparented itself into this window. - if (d->client) - d->rejectClient(event->xreparent.window); - else - d->acceptClient(event->xreparent.window); - } - break; - case ClientMessage: { - if (event->xclient.message_type == ATOM(_XEMBED)) { - // Ignore XEMBED messages not to ourselves - if (event->xclient.window != internalWinId()) - break; - - // Receiving an XEmbed message means the client - // is an XEmbed client. - d->clientIsXEmbed = true; - - Time msgtime = (Time) event->xclient.data.l[0]; - if (msgtime > X11->time) - X11->time = msgtime; - - switch (event->xclient.data.l[1]) { - case XEMBED_REQUEST_FOCUS: { - // This typically happens when the client gets focus - // because of a mouse click. - if (!hasFocus()) - setFocus(Qt::OtherFocusReason); - - // The message is passed along to the topmost container - // that eventually responds with a XEMBED_FOCUS_IN - // message. The focus in message is passed all the way - // back until it reaches the original focus - // requestor. In the end, not only the original client - // has focus, but also all its ancestor containers. - if (d->isEmbedded()) { - // If our window's embedded flag is set, then - // that suggests that we are part of a client. The - // parentWinId will then point to an container to whom - // we must pass this message. - sendXEmbedMessage(d->topLevelParentWinId(), x11Info().display(), XEMBED_REQUEST_FOCUS); - } else { - // Our window's embedded flag is not set, - // so we are the topmost container. We respond to - // the focus request message with a focus in - // message. This message will pass on from client - // to container to client until it reaches the - // originator of the XEMBED_REQUEST_FOCUS message. - sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT); - } - - break; - } - case XEMBED_FOCUS_NEXT: - // Client sends this event when it received a tab - // forward and was at the end of its focus chain. If - // we are the only widget in the focus chain, we send - // ourselves a FocusIn event. - if (d->focus_next != this) { - focusNextPrevChild(true); - } else { - QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason); - qApp->sendEvent(this, &event); - } - - break; - case XEMBED_FOCUS_PREV: - // Client sends this event when it received a backtab - // and was at the start of its focus chain. If we are - // the only widget in the focus chain, we send - // ourselves a FocusIn event. - if (d->focus_next != this) { - focusNextPrevChild(false); - } else { - QFocusEvent event(QEvent::FocusIn, Qt::BacktabFocusReason); - qApp->sendEvent(this, &event); - } - - break; - default: - break; - } - } - } - break; - case XButtonPress: - if (!d->clientIsXEmbed) { - setFocus(Qt::MouseFocusReason); - XAllowEvents(x11Info().display(), ReplayPointer, CurrentTime); - return true; - } - break; - case XButtonRelease: - if (!d->clientIsXEmbed) - XAllowEvents(x11Info().display(), SyncPointer, CurrentTime); - break; - default: - break; - } - - return QWidget::x11Event(event); -} - -/*! \internal - - Whenever the container is resized, we need to resize our client. -*/ -void QX11EmbedContainer::resizeEvent(QResizeEvent *) -{ - Q_D(QX11EmbedContainer); - if (d->client) - XResizeWindow(x11Info().display(), d->client, width(), height()); -} - -/*! \internal - - We use the QShowEvent to signal to our client that we want it to - map itself. We do this by changing its window property - XEMBED_INFO. The client will get an X11 PropertyNotify. -*/ -void QX11EmbedContainer::showEvent(QShowEvent *) -{ - Q_D(QX11EmbedContainer); - if (d->client) { - long data[] = {XEMBED_VERSION, XEMBED_MAPPED}; - XChangeProperty(x11Info().display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32, - PropModeReplace, (unsigned char *) data, 2); - } -} - -/*! \internal - - We use the QHideEvent to signal to our client that we want it to - unmap itself. We do this by changing its window property - XEMBED_INFO. The client will get an X11 PropertyNotify. -*/ -void QX11EmbedContainer::hideEvent(QHideEvent *) -{ - Q_D(QX11EmbedContainer); - if (d->client) { - long data[] = {XEMBED_VERSION, XEMBED_MAPPED}; - XChangeProperty(x11Info().display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32, - PropModeReplace, (unsigned char *) data, 2); - } -} - -/*! - \reimp -*/ -bool QX11EmbedContainer::event(QEvent *event) -{ - if (event->type() == QEvent::ParentChange) { - XSelectInput(x11Info().display(), internalWinId(), - KeyPressMask | KeyReleaseMask - | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask - | KeymapStateMask - | PointerMotionMask - | EnterWindowMask | LeaveWindowMask - | FocusChangeMask - | ExposureMask - | StructureNotifyMask - | SubstructureNotifyMask); - } - return QWidget::event(event); -} - -/*! \internal - - Rejects a client window by reparenting it to the root window. The - client will receive a reparentnotify, and will most likely assume - that the container has shut down. The XEmbed protocol does not - define any way to reject a client window, but this is a clean way - to do it. -*/ -void QX11EmbedContainerPrivate::rejectClient(WId window) -{ - Q_Q(QX11EmbedContainer); - q->setEnabled(false); - XRemoveFromSaveSet(q->x11Info().display(), client); - XReparentWindow(q->x11Info().display(), window, q->x11Info().appRootWindow(q->x11Info().screen()), 0, 0); -} - -/*! \internal - - Accepts a client by mapping it, resizing it and optionally - activating and giving it logical focusing through XEMBED messages. -*/ -void QX11EmbedContainerPrivate::acceptClient(WId window) -{ - Q_Q(QX11EmbedContainer); - client = window; - q->setEnabled(true); - - // This tells Qt that we wish to forward DnD messages to - // our client. - if (!extra) - createExtra(); - extraData()->xDndProxy = client; - - unsigned int version = XEmbedVersion(); - - Atom actual_type_return; - int actual_format_return; - unsigned long nitems_return = 0; - unsigned long bytes_after_return; - unsigned char *prop_return = 0; - unsigned int clientversion = 0; - - // Add this client to our saveset, so if we crash, the client window - // doesn't get destroyed. This is useful for containers that restart - // automatically after a crash, because it can simply reembed its clients - // without having to restart them (KDE panel). - XAddToSaveSet(q->x11Info().display(), client); - - // XEmbed clients have an _XEMBED_INFO property in which we can - // fetch the version - if (XGetWindowProperty(q->x11Info().display(), client, ATOM(_XEMBED_INFO), 0, 2, false, - ATOM(_XEMBED_INFO), &actual_type_return, &actual_format_return, - &nitems_return, &bytes_after_return, &prop_return) == Success) { - - if (actual_type_return != None && actual_format_return != 0) { - // Clients with the _XEMBED_INFO property are XEMBED clients. - clientIsXEmbed = true; - - long *p = (long *)prop_return; - if (nitems_return >= 2) - clientversion = (unsigned int)p[0]; - } - - XFree(prop_return); - } - - // Store client window's original size and placement. - Window root; - int x_return, y_return; - unsigned int width_return, height_return, border_width_return, depth_return; - XGetGeometry(q->x11Info().display(), client, &root, &x_return, &y_return, - &width_return, &height_return, &border_width_return, &depth_return); - clientOriginalRect.setCoords(x_return, y_return, - x_return + width_return - 1, - y_return + height_return - 1); - - // Ask the client for its minimum size. - XSizeHints size; - long msize; - if (XGetWMNormalHints(q->x11Info().display(), client, &size, &msize) && (size.flags & PMinSize)) { - wmMinimumSizeHint = QSize(size.min_width, size.min_height); - q->updateGeometry(); - } - - // The container should set the data2 field to the lowest of its - // supported version number and that of the client (from - // _XEMBED_INFO property). - unsigned int minversion = version > clientversion ? clientversion : version; - sendXEmbedMessage(client, q->x11Info().display(), XEMBED_EMBEDDED_NOTIFY, q->internalWinId(), minversion); - XMapWindow(q->x11Info().display(), client); - - // Resize it, but no smaller than its minimum size hint. - XResizeWindow(q->x11Info().display(), - client, - qMax(q->width(), wmMinimumSizeHint.width()), - qMax(q->height(), wmMinimumSizeHint.height())); - q->update(); - - // Not mentioned in the protocol is that if the container - // is already active, the client must be activated to work - // properly. - if (q->window()->isActiveWindow()) - sendXEmbedMessage(client, q->x11Info().display(), XEMBED_WINDOW_ACTIVATE); - - // Also, if the container already has focus, then it must - // send a focus in message to its new client; otherwise we ask - // it to remove focus. - if (q->focusWidget() == q && q->hasFocus()) - sendXEmbedMessage(client, q->x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST); - else - sendXEmbedMessage(client, q->x11Info().display(), XEMBED_FOCUS_OUT); - - if (!clientIsXEmbed) { - checkGrab(); - if (q->hasFocus()) { - XSetInputFocus(q->x11Info().display(), client, XRevertToParent, x11Time()); - } - } else { - if (!isEmbedded()) - moveInputToProxy(); - } - - emit q->clientIsEmbedded(); -} - -/*! \internal - - Moves X11 keyboard input focus to the focusProxy, unless the focus - is there already. When X11 keyboard input focus is on the - focusProxy, which is a child of the container and a sibling of the - client, X11 keypresses and keyreleases will always go to the proxy - and not to the client. -*/ -void QX11EmbedContainerPrivate::moveInputToProxy() -{ - Q_Q(QX11EmbedContainer); - // Following Owen Taylor's advice from the XEmbed specification to - // always use CurrentTime when no explicit user action is involved. - XSetInputFocus(q->x11Info().display(), focusProxy->internalWinId(), XRevertToParent, CurrentTime); -} - -/*! \internal - - Ask the window manager to give us a default minimum size. -*/ -QSize QX11EmbedContainer::minimumSizeHint() const -{ - Q_D(const QX11EmbedContainer); - if (!d->client || !d->wmMinimumSizeHint.isValid()) - return QWidget::minimumSizeHint(); - return d->wmMinimumSizeHint; -} - -/*! \internal - -*/ -void QX11EmbedContainerPrivate::checkGrab() -{ - Q_Q(QX11EmbedContainer); - if (!clientIsXEmbed && q->isActiveWindow() && !q->hasFocus()) { - if (!xgrab) { - XGrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId(), - true, ButtonPressMask, GrabModeSync, GrabModeAsync, - None, None); - } - xgrab = true; - } else { - if (xgrab) - XUngrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId()); - xgrab = false; - } -} - -/*! - Detaches the client from the embedder. The client will appear as a - standalone window on the desktop. -*/ -void QX11EmbedContainer::discardClient() -{ - Q_D(QX11EmbedContainer); - if (d->client) { - XResizeWindow(x11Info().display(), d->client, d->clientOriginalRect.width(), - d->clientOriginalRect.height()); - - d->rejectClient(d->client); - } -} - -QT_END_NAMESPACE |