1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qplatformdefs.h"
43 #include "qx11embed_x11.h"
44 #include <qapplication.h>
45 #include <qevent.h>
46 #include <qpainter.h>
47 #include <qlayout.h>
48 #include <qstyle.h>
49 #include <qstyleoption.h>
50 #include <qelapsedtimer.h>
51 #include <qpointer.h>
52 #include <qdebug.h>
53 #include <qx11info_x11.h>
54 #include <private/qt_x11_p.h>
55 #include <private/qwidget_p.h>
56 
57 #define XK_MISCELLANY
58 #define XK_LATIN1
59 #define None 0
60 #include <X11/Xlib.h>
61 #include <X11/Xatom.h>
62 #include <X11/Xutil.h>
63 #include <X11/keysymdef.h>
64 #include <X11/X.h>
65 
66 #ifndef XK_ISO_Left_Tab
67 #define XK_ISO_Left_Tab 0xFE20
68 #endif
69 
70 //#define QX11EMBED_DEBUG
71 #ifdef QX11EMBED_DEBUG
72 #include <qdebug.h>
73 #endif
74 
75 QT_BEGIN_NAMESPACE
76 
77 /*!
78     \class QX11EmbedWidget
79     \ingroup advanced
80 
81     \brief The QX11EmbedWidget class provides an XEmbed client widget.
82 
83     XEmbed is an X11 protocol that supports the embedding of a widget
84     from one application into another application.
85 
86     An XEmbed \e{client widget} is a window that is embedded into a
87     \e container. A container is the graphical location that embeds
88     (or \e swallows) an external application.
89 
90     QX11EmbedWidget is a widget used for writing XEmbed applets or
91     plugins. When it has been embedded and the container receives tab
92     focus, focus is passed on to the widget. When the widget reaches
93     the end of its focus chain, focus is passed back to the
94     container. Window activation, accelerator support, modality and
95     drag and drop (XDND) are also handled.
96 
97     The widget and container can both initiate the embedding. If the
98     widget is the initiator, the X11 window ID of the container that
99     it wants to embed itself into must be passed to embedInto().
100 
101     If the container initiates the embedding, the window ID of the
102     embedded widget must be known. The container calls embed(),
103     passing the window ID.
104 
105     This example shows an application that embeds a QX11EmbedWidget
106     subclass into the window whose ID is passed as a command-line
107     argument:
108 
109     \snippet doc/src/snippets/qx11embedwidget/main.cpp 0
110 
111     The problem of obtaining the window IDs is often solved by the
112     container invoking the application that provides the widget as a
113     separate process (as a panel invokes a docked applet), passing
114     its window ID to the new process as a command-line argument. The
115     new process can then call embedInto() with the container's window
116     ID, as shown in the example code above. Similarly, the new
117     process can report its window ID to the container through IPC, in
118     which case the container can embed the widget.
119 
120     When the widget has been embedded, it emits the signal
121     embedded(). If it is closed by the container, the widget emits
122     containerClosed(). If an error occurs when embedding, error() is
123     emitted.
124 
125     There are XEmbed widgets available for KDE and GTK+. The GTK+
126     equivalent of QX11EmbedWidget is GtkPlug. The corresponding KDE 3
127     widget is called QXEmbed.
128 
129     \sa QX11EmbedContainer, {XEmbed Specification}
130 */
131 
132 /*!
133     \class QX11EmbedContainer
134     \ingroup advanced
135 
136     \brief The QX11EmbedContainer class provides an XEmbed container
137     widget.
138 
139     XEmbed is an X11 protocol that supports the embedding of a widget
140     from one application into another application.
141 
142     An XEmbed \e container is the graphical location that embeds an
143     external \e {client widget}. A client widget is a window that is
144     embedded into a container.
145 
146     When a widget has been embedded and the container receives tab
147     focus, focus is passed on to the widget. When the widget reaches
148     the end of its focus chain, focus is passed back to the
149     container. Window activation, accelerator support, modality and
150     drag and drop (XDND) are also handled.
151 
152     QX11EmbedContainer is commonly used for writing panels or
153     toolbars that hold applets, or for \e swallowing X11
154     applications. When writing a panel application, one container
155     widget is created on the toolbar, and it can then either swallow
156     another widget using embed(), or allow an XEmbed widget to be
157     embedded into itself. The container's X11 window ID, which is
158     retrieved with winId(), must then be known to the client widget.
159     After embedding, the client's window ID can be retrieved with
160     clientWinId().
161 
162     In the following example, a container widget is created as the
163     main widget. It then invokes an application called "playmovie",
164     passing its window ID as a command line argument. The "playmovie"
165     program is an XEmbed client widget. The widget embeds itself into
166     the container using the container's window ID.
167 
168     \snippet doc/src/snippets/qx11embedcontainer/main.cpp 0
169 
170     When the client widget is embedded, the container emits the
171     signal clientIsEmbedded(). The signal clientClosed() is emitted
172     when a widget is closed.
173 
174     It is possible for QX11EmbedContainer to embed XEmbed widgets
175     from toolkits other than Qt, such as GTK+. Arbitrary (non-XEmbed)
176     X11 widgets can also be embedded, but the XEmbed-specific
177     features such as window activation and focus handling are then
178     lost.
179 
180     The GTK+ equivalent of QX11EmbedContainer is GtkSocket. The
181     corresponding KDE 3 widget is called QXEmbed.
182 
183     \sa QX11EmbedWidget, {XEmbed Specification}
184 */
185 
186 /*! \fn void QX11EmbedWidget::embedded()
187 
188     This signal is emitted by the widget that has been embedded by an
189     XEmbed container.
190 */
191 
192 /*! \fn void QX11EmbedWidget::containerClosed()
193 
194     This signal is emitted by the client widget when the container
195     closes the widget. This can happen if the container itself
196     closes, or if the widget is rejected.
197 
198     The container can reject a widget for any reason, but the most
199     common cause of a rejection is when an attempt is made to embed a
200     widget into a container that already has an embedded widget.
201 */
202 
203 /*! \fn void QX11EmbedContainer::clientIsEmbedded()
204 
205     This signal is emitted by the container when a client widget has
206     been embedded.
207 */
208 
209 /*! \fn void QX11EmbedContainer::clientClosed()
210 
211     This signal is emitted by the container when the client widget
212     closes.
213 */
214 
215 /*!
216     \fn void QX11EmbedWidget::error(QX11EmbedWidget::Error error)
217 
218     This signal is emitted if an error occurred as a result of
219     embedding into or communicating with a container. The specified
220     \a error describes the problem that occurred.
221 
222     \sa QX11EmbedWidget::Error
223 */
224 
225 /*!
226     \fn QX11EmbedContainer::Error QX11EmbedContainer::error() const
227 
228     Returns the last error that occurred.
229 */
230 
231 /*! \fn void QX11EmbedContainer::error(QX11EmbedContainer::Error error)
232 
233     This signal is emitted if an error occurred when embedding or
234     communicating with a client. The specified \a error describes the
235     problem that occurred.
236 
237     \sa QX11EmbedContainer::Error
238 */
239 
240 /*!
241     \enum QX11EmbedWidget::Error
242 
243     \value Unknown An unrecognized error occurred.
244 
245     \value InvalidWindowID The X11 window ID of the container was
246         invalid. This error is usually triggered by passing an invalid
247         window ID to embedInto().
248 
249     \omitvalue Internal
250 */
251 
252 /*!
253     \enum QX11EmbedContainer::Error
254 
255     \value Unknown An unrecognized error occurred.
256 
257     \value InvalidWindowID The X11 window ID of the container was
258         invalid. This error is usually triggered by passing an invalid
259         window ID to embed().
260 
261     \omitvalue Internal
262 */
263 
264 const int XButtonPress = ButtonPress;
265 const int XButtonRelease = ButtonRelease;
266 #undef ButtonPress
267 #undef ButtonRelease
268 
269 // This is a hack to move topData() out from QWidgetPrivate to public.  We
270 // need to to inspect window()'s embedded state.
271 class QHackWidget : public QWidget
272 {
273     Q_DECLARE_PRIVATE(QWidget)
274 public:
topData()275     QTLWExtra* topData() { return d_func()->topData(); }
276 };
277 
278 static unsigned int XEMBED_VERSION = 0;
279 
280 enum QX11EmbedMessageType {
281     XEMBED_EMBEDDED_NOTIFY = 0,
282     XEMBED_WINDOW_ACTIVATE = 1,
283     XEMBED_WINDOW_DEACTIVATE = 2,
284     XEMBED_REQUEST_FOCUS = 3,
285     XEMBED_FOCUS_IN = 4,
286     XEMBED_FOCUS_OUT = 5,
287     XEMBED_FOCUS_NEXT = 6,
288     XEMBED_FOCUS_PREV = 7,
289     XEMBED_MODALITY_ON = 10,
290     XEMBED_MODALITY_OFF = 11,
291     XEMBED_REGISTER_ACCELERATOR = 12,
292     XEMBED_UNREGISTER_ACCELERATOR = 13,
293     XEMBED_ACTIVATE_ACCELERATOR = 14
294 };
295 
296 enum QX11EmbedFocusInDetail {
297     XEMBED_FOCUS_CURRENT = 0,
298     XEMBED_FOCUS_FIRST = 1,
299     XEMBED_FOCUS_LAST = 2
300 };
301 
302 enum QX11EmbedFocusInFlags {
303     XEMBED_FOCUS_OTHER = (0 << 0),
304     XEMBED_FOCUS_WRAPAROUND = (1 << 0)
305 };
306 
307 enum QX11EmbedInfoFlags {
308     XEMBED_MAPPED = (1 << 0)
309 };
310 
311 enum QX11EmbedAccelModifiers {
312     XEMBED_MODIFIER_SHIFT = (1 << 0),
313     XEMBED_MODIFIER_CONTROL = (1 << 1),
314     XEMBED_MODIFIER_ALT = (1 << 2),
315     XEMBED_MODIFIER_SUPER = (1 << 3),
316     XEMBED_MODIFIER_HYPER = (1 << 4)
317 };
318 
319 enum QX11EmbedAccelFlags {
320     XEMBED_ACCELERATOR_OVERLOADED = (1 << 0)
321 };
322 
323 // Silence the default X11 error handler.
x11ErrorHandler(Display *,XErrorEvent *)324 static int x11ErrorHandler(Display *, XErrorEvent *)
325 {
326     return 0;
327 }
328 
329 // Returns the X11 timestamp. Maintained mainly by qapplication
330 // internals, but also updated by the XEmbed widgets.
x11Time()331 static Time x11Time()
332 {
333     return qt_x11Data->time;
334 }
335 
336 // Gives the version and flags of the supported XEmbed protocol.
XEmbedVersion()337 static unsigned int XEmbedVersion()
338 {
339     return 0;
340 }
341 
342 // Sends an XEmbed message.
sendXEmbedMessage(WId window,Display * display,long message,long detail=0,long data1=0,long data2=0)343 static void sendXEmbedMessage(WId window, Display *display, long message,
344                   long detail = 0, long data1 = 0, long data2 = 0)
345 {
346     XClientMessageEvent c;
347     memset(&c, 0, sizeof(c));
348     c.type = ClientMessage;
349     c.message_type = ATOM(_XEMBED);
350     c.format = 32;
351     c.display = display;
352     c.window = window;
353 
354     c.data.l[0] = x11Time();
355     c.data.l[1] = message;
356     c.data.l[2] = detail;
357     c.data.l[3] = data1;
358     c.data.l[4] = data2;
359 
360     XSendEvent(display, window, false, NoEventMask, (XEvent *) &c);
361 }
362 
363 // From qapplication_x11.cpp
364 static XKeyEvent lastKeyEvent;
365 
366 static QCoreApplication::EventFilter oldX11EventFilter;
367 
368 // The purpose of this global x11 filter is for one to capture the key
369 // events in their original state, but most importantly this is the
370 // only way to get the WM_TAKE_FOCUS message from WM_PROTOCOLS.
x11EventFilter(void * message,long * result)371 static bool x11EventFilter(void *message, long *result)
372 {
373     XEvent *event = reinterpret_cast<XEvent *>(message);
374     if (event->type == XKeyPress || event->type == XKeyRelease)
375 	lastKeyEvent = event->xkey;
376 
377     if (oldX11EventFilter && oldX11EventFilter != &x11EventFilter)
378 	return oldX11EventFilter(message, result);
379     else
380 	return false;
381 }
382 
383 //
384 struct functorData
385 {
386     Window id, rootWindow;
387     bool clearedWmState;
388     bool reparentedToRoot;
389 };
390 
functor(Display * display,XEvent * event,XPointer arg)391 static Bool functor(Display *display, XEvent *event, XPointer arg)
392 {
393     functorData *data = (functorData *) arg;
394 
395     if (!data->reparentedToRoot && event->type == ReparentNotify
396         && event->xreparent.window == data->id
397         && event->xreparent.parent == data->rootWindow) {
398         data->reparentedToRoot = true;
399         return true;
400     }
401 
402     if (!data->clearedWmState
403         && event->type == PropertyNotify
404         && event->xproperty.window == data->id
405         && event->xproperty.atom == ATOM(WM_STATE)) {
406 	if (event->xproperty.state == PropertyDelete) {
407             data->clearedWmState = true;
408             return true;
409         }
410 
411 	Atom ret;
412 	int format, status;
413 	unsigned char *retval;
414 	unsigned long nitems, after;
415 	status = XGetWindowProperty(display, data->id, ATOM(WM_STATE), 0, 2, False, ATOM(WM_STATE),
416 				    &ret, &format, &nitems, &after, &retval );
417 	if (status == Success && ret == ATOM(WM_STATE) && format == 32 && nitems > 0) {
418             long state = *(long *)retval;
419             XFree(retval);
420             if (state == WithdrawnState) {
421                 data->clearedWmState = true;
422 		return true;
423             }
424 	}
425     }
426 
427     return false;
428 }
429 
430 class QX11EmbedWidgetPrivate : public QWidgetPrivate
431 {
432     Q_DECLARE_PUBLIC(QX11EmbedWidget)
433 public:
QX11EmbedWidgetPrivate()434     inline QX11EmbedWidgetPrivate()
435     {
436         lastError = QX11EmbedWidget::Unknown;
437         container = 0;
438     }
439 
440     void setEmbedded();
441 
emitError(QX11EmbedWidget::Error error)442     void emitError(QX11EmbedWidget::Error error) {
443         Q_Q(QX11EmbedWidget);
444 
445         lastError = error;
446         emit q->error(error);
447     }
448 
449     enum FocusWidgets {
450         FirstFocusWidget,
451         LastFocusWidget
452     };
453 
454     int focusOriginator;
455     QWidget *getFocusWidget(FocusWidgets fw);
456     void checkActivateWindow(QObject *o);
457     QX11EmbedWidget *xEmbedWidget(QObject *o) const;
458     void clearFocus();
459 
460     WId container;
461     QPointer<QWidget> currentFocus;
462 
463     QX11EmbedWidget::Error lastError;
464 
465 };
466 
467 /*!
468     Constructs a QX11EmbedWidget object with the given \a parent.
469 */
QX11EmbedWidget(QWidget * parent)470 QX11EmbedWidget::QX11EmbedWidget(QWidget *parent)
471     : QWidget(*new QX11EmbedWidgetPrivate, parent, 0)
472 {
473     XSetErrorHandler(x11ErrorHandler);
474 
475     setAttribute(Qt::WA_NativeWindow);
476     setAttribute(Qt::WA_DontCreateNativeAncestors);
477     createWinId();
478     XSelectInput(x11Info().display(), internalWinId(),
479                  KeyPressMask | KeyReleaseMask | ButtonPressMask
480                     | ButtonReleaseMask
481                     | KeymapStateMask | ButtonMotionMask | PointerMotionMask
482                     | FocusChangeMask
483                     | ExposureMask | StructureNotifyMask
484                     | SubstructureNotifyMask | PropertyChangeMask);
485 
486     long data[] = {XEMBED_VERSION, XEMBED_MAPPED};
487     XChangeProperty(x11Info().display(), internalWinId(), ATOM(_XEMBED_INFO),
488                     ATOM(_XEMBED_INFO), 32, PropModeReplace,
489                     (unsigned char*) data, 2);
490 
491     setFocusPolicy(Qt::StrongFocus);
492     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
493     QApplication::instance()->installEventFilter(this);
494 
495     // Focus itself at first.
496     qApp->setActiveWindow(this);
497 
498 #ifdef QX11EMBED_DEBUG
499     qDebug() << "QX11EmbedWidget::QX11EmbedWidget: constructed client"
500              << (void *)this << "with winId" << winId();
501 #endif
502 }
503 
504 /*!
505     Destructs the QX11EmbedWidget object. If the widget is embedded
506     when deleted, it is hidden and then detached from its container,
507     so that the container is free to embed a new widget.
508 */
~QX11EmbedWidget()509 QX11EmbedWidget::~QX11EmbedWidget()
510 {
511     Q_D(QX11EmbedWidget);
512     if (d->container) {
513 #ifdef QX11EMBED_DEBUG
514         qDebug() << "QX11EmbedWidget::~QX11EmbedWidget: unmapping"
515                  << (void *)this << "with winId" << winId()
516                  << "from container with winId" << d->container;
517 #endif
518         XUnmapWindow(x11Info().display(), internalWinId());
519         XReparentWindow(x11Info().display(), internalWinId(), x11Info().appRootWindow(x11Info().screen()), 0, 0);
520     }
521 
522 #ifdef QX11EMBED_DEBUG
523     qDebug() << "QX11EmbedWidget::~QX11EmbedWidget: destructed client"
524              << (void *)this << "with winId" << winId();
525 #endif
526 }
527 
528 /*!
529     Returns the type of error that occurred last. This is the same error code
530     that is emitted by the error() signal.
531 
532     \sa Error
533 */
error() const534 QX11EmbedWidget::Error QX11EmbedWidget::error() const
535 {
536     return d_func()->lastError;
537 }
538 
539 /*!
540     When this function is called, the widget embeds itself into the
541     container whose window ID is \a id.
542 
543     If \a id is \e not the window ID of a container this function will
544     behave unpredictably.
545 */
embedInto(WId id)546 void QX11EmbedWidget::embedInto(WId id)
547 {
548     Q_D(QX11EmbedWidget);
549 #ifdef QX11EMBED_DEBUG
550     qDebug() << "QX11EmbedWidget::embedInto: embedding client"
551              << (void *)this << "with winId" << winId() << "into container"
552              << id;
553 #endif
554 
555     d->container = id;
556     switch (XReparentWindow(x11Info().display(), internalWinId(), d->container, 0, 0)) {
557     case BadWindow:
558         d->emitError(InvalidWindowID);
559         break;
560     case BadMatch:
561         d->emitError(Internal);
562         break;
563     case Success:
564     default:
565         break;
566     }
567     QTLWExtra* x = d->extra ? d->extra->topextra : 0;
568     if (x)
569         x->frameStrut.setCoords(0, 0, 0, 0);
570     d->data.fstrut_dirty = false;
571 }
572 
573 /*! \internal
574 
575     Gets the first or last child widget that can get focus.
576 */
getFocusWidget(FocusWidgets fw)577 QWidget *QX11EmbedWidgetPrivate::getFocusWidget(FocusWidgets fw)
578 {
579     Q_Q(QX11EmbedWidget);
580     QWidget *tlw = q;
581     QWidget *w = tlw->nextInFocusChain();
582 
583     QWidget *last = tlw;
584 
585     extern bool qt_tab_all_widgets;
586     uint focus_flag = qt_tab_all_widgets ? Qt::TabFocus : Qt::StrongFocus;
587 
588     while (w != tlw)
589     {
590         if (((w->focusPolicy() & focus_flag) == focus_flag)
591             && w->isVisibleTo(q) && w->isEnabled())
592         {
593             last = w;
594             if (fw == FirstFocusWidget)
595                 break;
596         }
597         w = w->nextInFocusChain();
598     }
599 
600     return last;
601 }
602 
603 /*! \internal
604 
605     Returns the xembed widget that the widget is a child of
606 */
xEmbedWidget(QObject * o) const607 QX11EmbedWidget *QX11EmbedWidgetPrivate::xEmbedWidget(QObject *o) const
608 {
609     QX11EmbedWidget *xec = 0;
610 
611     // Check the widget itself, then its parents, and find the first
612     // QX11EmbedWidget.
613     do {
614         if ((xec = qobject_cast<QX11EmbedWidget *>(o)))
615             return xec;
616     } while ((o = o->parent()));
617     return 0;
618 }
619 
620 /*! \internal
621 
622     Checks the active window.
623 */
checkActivateWindow(QObject * o)624 void QX11EmbedWidgetPrivate::checkActivateWindow(QObject *o)
625 {
626     Q_Q(QX11EmbedWidget);
627     QX11EmbedWidget *xec = xEmbedWidget(o);
628 
629     // check if we are in the right xembed client
630     if (q != xec)
631         return;
632 
633     QWidget *w = qobject_cast<QWidget *>(o);
634 
635     // if it is no active window, then don't do the change
636     if (!(w && qApp->activeWindow()))
637         return;
638 
639     // if it already is the active window, don't do anything
640     if (w->window() != qApp->activeWindow())
641     {
642         qApp->setActiveWindow(w->window());
643         currentFocus = w;
644 
645         sendXEmbedMessage(xec->containerWinId(), q->x11Info().display(), XEMBED_REQUEST_FOCUS);
646     }
647 }
648 
649 /*! \internal
650 
651     Clears the focus
652 */
clearFocus()653 void QX11EmbedWidgetPrivate::clearFocus()
654 {
655     Q_Q(QX11EmbedWidget);
656     // Setting focus on the client itself removes Qt's
657     // logical focus rectangle. We can't just do a
658     // clearFocus here, because when we send the synthetic
659     // FocusIn to ourselves on activation, Qt will set
660     // focus on focusWidget() again. This way, we "hide"
661     // focus rather than clearing it.
662 
663     if (!q->window()->hasFocus())
664         q->window()->setFocus(Qt::OtherFocusReason);
665 
666     currentFocus = 0;
667 }
668 
669 /*! \internal
670 
671     Sets the embedded flag on the client.
672 */
setEmbedded()673 void QX11EmbedWidgetPrivate::setEmbedded()
674 {
675     Q_Q(QX11EmbedWidget);
676     ((QHackWidget *)q->window())->topData()->embedded = 1;
677 }
678 
679 /*! \internal
680 
681     Handles WindowActivate and FocusIn events for the client.
682 */
eventFilter(QObject * o,QEvent * event)683 bool QX11EmbedWidget::eventFilter(QObject *o, QEvent *event)
684 {
685     Q_D(QX11EmbedWidget);
686     switch (event->type()) {
687     case QEvent::FocusIn:
688         switch (((QFocusEvent *)event)->reason()) {
689         case Qt::MouseFocusReason:
690             // If the user clicks into one of the client widget's
691             // children and we didn't have focus already, we request
692             // focus from our container.
693             if (d->xEmbedWidget(o) == this) {
694                 if (d->currentFocus.isNull())
695                     sendXEmbedMessage(d->container, x11Info().display(), XEMBED_REQUEST_FOCUS);
696 
697                 d->currentFocus = qobject_cast<QWidget *>(o);
698             }
699             break;
700         case Qt::TabFocusReason:
701             // If the xembed client receives a focus event because of
702             // a Tab, then we are at the end of our focus chain and we
703             // ask the container to move to its next focus widget.
704             if (o == this) {
705                 d->clearFocus();
706                 sendXEmbedMessage(d->container, x11Info().display(), XEMBED_FOCUS_NEXT);
707                 return true;
708             } else {
709                 // We're listening on events from qApp, so in order
710                 // for us to know who to set focus on if we receive an
711                 // activation event, we note the widget that got the
712                 // focusin last.
713                 if (d->xEmbedWidget(o) == this)
714                     d->currentFocus = qobject_cast<QWidget *>(o);
715             }
716             break;
717         case Qt::BacktabFocusReason:
718             // If the window receives a focus event because of
719             // a Backtab, then we are at the start of our focus chain
720             // and we ask the container to move to its previous focus
721             // widget.
722             if (o == this) {
723                 // See comment for Tab.
724                 // If we receive a XEMBED_FOCUS_IN
725                 // XEMBED_FOCUS_CURRENT, we will set focus in
726                 // currentFocus. To avoid that in this case, we reset
727                 // currentFocus.
728                 d->clearFocus();
729                 sendXEmbedMessage(d->container, x11Info().display(), XEMBED_FOCUS_PREV);
730                 return true;
731             } else {
732                 if (d->xEmbedWidget(o) == this)
733                     d->currentFocus = qobject_cast<QWidget *>(o);
734             }
735             break;
736         case Qt::ActiveWindowFocusReason:
737             if (isActiveWindow()) {
738                 if (!d->currentFocus.isNull()) {
739                     if (!d->currentFocus->hasFocus())
740                         d->currentFocus->setFocus(Qt::OtherFocusReason);
741                 } else {
742                     d->clearFocus();
743                     return true;
744                 }
745             }
746 
747             break;
748         case Qt::PopupFocusReason:
749         case Qt::ShortcutFocusReason:
750         case Qt::OtherFocusReason:
751             // If focus is received to any child widget because of any
752             // other reason, remember the widget so that we can give
753             // it focus again if we're activated.
754             if (d->xEmbedWidget(o) == this) {
755                d->currentFocus = qobject_cast<QWidget *>(o);
756             }
757             break;
758         default:
759             break;
760         }
761         break;
762     case QEvent::MouseButtonPress:
763         // If we get a mouse button press event inside a embedded widget
764         // make sure this is the active window in qapp.
765         d->checkActivateWindow(o);
766         break;
767     default:
768         break;
769     }
770 
771     return QWidget::eventFilter(o, event);
772 }
773 
774 /*! \internal
775 
776     Handles some notification events and client messages. Client side
777     XEmbed message receiving is also handled here.
778 */
x11Event(XEvent * event)779 bool QX11EmbedWidget::x11Event(XEvent *event)
780 {
781     Q_D(QX11EmbedWidget);
782     switch (event->type) {
783     case DestroyNotify:
784 #ifdef QX11EMBED_DEBUG
785         qDebug() << "QX11EmbedWidget::x11Event: client"
786                  << (void *)this << "with winId" << winId()
787                  << "received a DestroyNotify";
788 #endif
789         // If the container window is destroyed, we signal this to the user.
790         d->container = 0;
791         emit containerClosed();
792         break;
793     case ReparentNotify:
794 #ifdef QX11EMBED_DEBUG
795         qDebug() << "QX11EmbedWidget::x11Event: client"
796                  << (void *)this << "with winId" << winId()
797                  << "received a ReparentNotify to"
798                  << ((event->xreparent.parent == x11Info().appRootWindow(x11Info().screen()))
799                      ? QString::fromLatin1("root") : QString::number(event->xreparent.parent));
800 #endif
801         // If the container shuts down, we will be reparented to the
802         // root window. We must also consider the case that we may be
803         // reparented from one container to another.
804         if (event->xreparent.parent == x11Info().appRootWindow(x11Info().screen())) {
805             if (((QHackWidget *)this)->topData()->embedded) {
806                 d->container = 0;
807                 emit containerClosed();
808             }
809             return true;
810         } else {
811             d->container = event->xreparent.parent;
812         }
813         break;
814     case UnmapNotify:
815         // Mapping and unmapping are handled by changes to the
816         // _XEMBED_INFO property. Any default map/unmap requests are
817         // ignored.
818         return true;
819     case PropertyNotify:
820         // The container sends us map/unmap messages through the
821         // _XEMBED_INFO property. We adhere to the XEMBED_MAPPED bit in
822         // data2.
823         if (event->xproperty.atom == ATOM(_XEMBED_INFO)) {
824             Atom actual_type_return;
825             int actual_format_return;
826             unsigned long nitems_return;
827             unsigned long bytes_after_return;
828             unsigned char *prop_return = 0;
829             if (XGetWindowProperty(x11Info().display(), internalWinId(), ATOM(_XEMBED_INFO), 0, 2,
830                                    false, ATOM(_XEMBED_INFO), &actual_type_return,
831                                    &actual_format_return, &nitems_return,
832                                    &bytes_after_return, &prop_return) == Success) {
833                 if (nitems_return > 1) {
834                     if (((long * )prop_return)[1] & XEMBED_MAPPED) {
835                         XMapWindow(x11Info().display(), internalWinId());
836                     } else {
837                         XUnmapWindow(x11Info().display(), internalWinId());
838                     }
839                 }
840                 if (prop_return)
841                     XFree(prop_return);
842             }
843         }
844 
845         break;
846     case ClientMessage:
847         // XEMBED messages have message_type _XEMBED
848         if (event->xclient.message_type == ATOM(_XEMBED)) {
849             // Discard XEMBED messages not to ourselves. (### dead code?)
850             if (event->xclient.window != internalWinId())
851                 break;
852 
853             // Update qt_x_time if necessary
854             Time msgtime = (Time) event->xclient.data.l[0];
855             if (msgtime > X11->time)
856                 X11->time = msgtime;
857 
858             switch (event->xclient.data.l[1]) {
859             case XEMBED_WINDOW_ACTIVATE: {
860                 // When we receive an XEMBED_WINDOW_ACTIVATE, we simply send
861                 // ourselves a WindowActivate event. Real activation happens
862                 // when receive XEMBED_FOCUS_IN.
863                 if (!isActiveWindow()) {
864                     QEvent ev(QEvent::WindowActivate);
865                     QApplication::sendEvent(this, &ev);
866                 }
867             }
868                 break;
869             case XEMBED_WINDOW_DEACTIVATE: {
870                 // When we receive an XEMBED_WINDOW_DEACTIVATE, we simply send
871                 // ourselves a WindowDeactivate event. Real activation happens
872                 // when receive XEMBED_FOCUS_IN.
873                 if (isActiveWindow()) {
874                     if (!qApp->activePopupWidget())
875                         QApplication::setActiveWindow(0);
876                 } else {
877                     QEvent ev(QEvent::WindowDeactivate);
878                     QApplication::sendEvent(this, &ev);
879                 }
880             }
881                 break;
882             case XEMBED_EMBEDDED_NOTIFY: {
883 #ifdef QX11EMBED_DEBUG
884                 qDebug() << "QX11EmbedWidget::x11Event: client"
885                          << (void *)this << "with winId" << winId()
886                          << "received an XEMBED EMBEDDED NOTIFY message";
887 #endif
888                 // In this message's l[2] we have the max version
889                 // supported by both the client and the
890                 // container. QX11EmbedWidget does not use this field.
891 
892                 // We have been embedded, so we set our
893                 // client's embedded flag.
894                 d->setEmbedded();
895                 emit embedded();
896             }
897                 break;
898             case XEMBED_FOCUS_IN:
899                 // don't set the focus if a modal dialog is open
900                 if (qApp->activeModalWidget())
901                     break;
902 
903                 // in case we embed more than one topLevel window inside the same
904                 // host window.
905                 if (window() != qApp->activeWindow())
906                     qApp->setActiveWindow(this);
907 
908                 switch (event->xclient.data.l[2]) {
909                 case XEMBED_FOCUS_CURRENT:
910                     // The container sends us this message if it wants
911                     // us to focus on the widget that last had focus.
912                     // This is the reply when XEMBED_REQUEST_FOCUS is
913                     // sent to the container.
914                     if (!d->currentFocus.isNull()) {
915                         if (!d->currentFocus->hasFocus())
916                             d->currentFocus->setFocus(Qt::OtherFocusReason);
917                     } else {
918                         // No widget currently has focus. We set focus
919                         // on the first widget next to the
920                         // client widget. Since the setFocus will not work
921                         // if the window is disabled, set the currentFocus
922                         // directly so that it's set on window activate.
923                         d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::FirstFocusWidget);
924                         d->currentFocus->setFocus(Qt::OtherFocusReason);
925                     }
926                     break;
927                 case XEMBED_FOCUS_FIRST:
928                     // The container sends this message when it wants
929                     // us to focus on the first widget in our focus
930                     // chain (typically because of a tab).
931                     d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::FirstFocusWidget);
932                     d->currentFocus->setFocus(Qt::TabFocusReason);
933                     break;
934                 case XEMBED_FOCUS_LAST:
935                     // The container sends this message when it wants
936                     // us to focus on the last widget in our focus
937                     // chain (typically because of a backtab).
938                     d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::LastFocusWidget);
939                     d->currentFocus->setFocus(Qt::BacktabFocusReason);
940                     break;
941                 default:
942                     // Ignore any other XEMBED_FOCUS_IN details.
943                     break;
944                 }
945                 break;
946             case XEMBED_FOCUS_OUT:
947                 // The container sends us this message when it wants us
948                 // to lose focus and forget about the widget that last
949                 // had focus. Typically sent by the container when it
950                 // loses focus because of mouse or tab activity. We do
951                 // then not want to set focus on anything if we're
952                 // activated.
953                 if (isActiveWindow())
954                     d->clearFocus();
955 
956                 break;
957             default:
958                 // Ignore any other XEMBED messages.
959                 break;
960             };
961         } else {
962             // Non-XEMBED client messages are not interesting.
963         }
964 
965         break;
966     default:
967         // Ignore all other x11 events.
968         break;
969     }
970 
971     // Allow default handling.
972     return QWidget::x11Event(event);
973 }
974 
975 /*!
976     \reimp
977 */
event(QEvent * event)978 bool QX11EmbedWidget::event(QEvent *event)
979 {
980     if (event->type() == QEvent::ParentChange) {
981         XSelectInput(x11Info().display(), internalWinId(),
982                      KeyPressMask | KeyReleaseMask | ButtonPressMask
983                      | ButtonReleaseMask
984                      | KeymapStateMask | ButtonMotionMask | PointerMotionMask
985                      | FocusChangeMask
986                      | ExposureMask | StructureNotifyMask
987                      | SubstructureNotifyMask | PropertyChangeMask);
988     }
989     return QWidget::event(event);
990 }
991 
992 /*!
993     \reimp
994 */
resizeEvent(QResizeEvent * event)995 void QX11EmbedWidget::resizeEvent(QResizeEvent *event)
996 {
997     if (layout())
998         layout()->update();
999     QWidget::resizeEvent(event);
1000 }
1001 
1002 /*!
1003     If the widget is embedded, returns the window ID of the
1004     container; otherwize returns 0.
1005 */
containerWinId() const1006 WId QX11EmbedWidget::containerWinId() const
1007 {
1008     Q_D(const QX11EmbedWidget);
1009     return d->container;
1010 }
1011 
1012 class QX11EmbedContainerPrivate : public QWidgetPrivate
1013 {
1014     Q_DECLARE_PUBLIC(QX11EmbedContainer)
1015 public:
QX11EmbedContainerPrivate()1016     inline QX11EmbedContainerPrivate()
1017     {
1018         lastError = QX11EmbedContainer::Unknown;
1019         client = 0;
1020         focusProxy = 0;
1021         clientIsXEmbed = false;
1022         xgrab = false;
1023     }
1024 
1025     bool isEmbedded() const;
1026     void moveInputToProxy();
1027 
1028     void acceptClient(WId window);
1029     void rejectClient(WId window);
1030 
1031     void checkGrab();
1032 
1033     WId topLevelParentWinId() const;
1034 
emitError(QX11EmbedContainer::Error error)1035     void emitError(QX11EmbedContainer::Error error) {
1036         Q_Q(QX11EmbedContainer);
1037         lastError = error;
1038         emit q->error(error);
1039     }
1040 
1041     WId client;
1042     QWidget *focusProxy;
1043     bool clientIsXEmbed;
1044     bool xgrab;
1045     QRect clientOriginalRect;
1046     QSize wmMinimumSizeHint;
1047 
1048     QX11EmbedContainer::Error lastError;
1049 
1050     static QX11EmbedContainer *activeContainer;
1051 };
1052 
1053 QX11EmbedContainer *QX11EmbedContainerPrivate::activeContainer = 0;
1054 
1055 /*!
1056     Creates a QX11EmbedContainer object with the given \a parent.
1057 */
QX11EmbedContainer(QWidget * parent)1058 QX11EmbedContainer::QX11EmbedContainer(QWidget *parent)
1059     : QWidget(*new QX11EmbedContainerPrivate, parent, 0)
1060 {
1061     Q_D(QX11EmbedContainer);
1062     XSetErrorHandler(x11ErrorHandler);
1063 
1064     setAttribute(Qt::WA_NativeWindow);
1065     setAttribute(Qt::WA_DontCreateNativeAncestors);
1066     createWinId();
1067 
1068     setFocusPolicy(Qt::StrongFocus);
1069     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
1070     // ### PORT setKeyCompression(false);
1071     setAcceptDrops(true);
1072     setEnabled(false);
1073 
1074     // Everybody gets a focus proxy, but only one toplevel container's
1075     // focus proxy is actually in use.
1076     d->focusProxy = new QWidget(this);
1077     d->focusProxy->setAttribute(Qt::WA_NativeWindow);
1078     d->focusProxy->setAttribute(Qt::WA_DontCreateNativeAncestors);
1079     d->focusProxy->createWinId();
1080     d->focusProxy->setGeometry(-1, -1, 1, 1);
1081 
1082     // We need events from the window (activation status) and
1083     // from qApp (keypress/release).
1084     qApp->installEventFilter(this);
1085 
1086     // Install X11 event filter.
1087     if (!oldX11EventFilter)
1088         oldX11EventFilter = QCoreApplication::instance()->setEventFilter(x11EventFilter);
1089 
1090     XSelectInput(x11Info().display(), internalWinId(),
1091                  KeyPressMask | KeyReleaseMask
1092                  | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
1093                  | KeymapStateMask
1094                  | PointerMotionMask
1095                  | EnterWindowMask | LeaveWindowMask
1096                  | FocusChangeMask
1097                  | ExposureMask
1098                  | StructureNotifyMask
1099                  | SubstructureNotifyMask);
1100 
1101     // Make sure our new event mask takes effect as soon as possible.
1102     XFlush(x11Info().display());
1103 
1104     // Move input to our focusProxy if this widget is active, and not
1105     // shaded by a modal dialog (in which case isActiveWindow() would
1106     // still return true, but where we must not move input focus).
1107     if (qApp->activeWindow() == window() && !d->isEmbedded())
1108         d->moveInputToProxy();
1109 
1110 #ifdef QX11EMBED_DEBUG
1111     qDebug() << "QX11EmbedContainer::QX11EmbedContainer: constructed container"
1112              << (void *)this << "with winId" << winId();
1113 #endif
1114 }
1115 
1116 /*!
1117     Destructs a QX11EmbedContainer.
1118 */
~QX11EmbedContainer()1119 QX11EmbedContainer::~QX11EmbedContainer()
1120 {
1121     Q_D(QX11EmbedContainer);
1122     if (d->client) {
1123 	XUnmapWindow(x11Info().display(), d->client);
1124 	XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(x11Info().screen()), 0, 0);
1125     }
1126 
1127     if (d->xgrab)
1128 	XUngrabButton(x11Info().display(), AnyButton, AnyModifier, internalWinId());
1129 }
1130 
1131 
error() const1132 QX11EmbedContainer::Error QX11EmbedContainer::error() const {
1133     return d_func()->lastError;
1134 }
1135 
1136 
1137 /*! \reimp
1138 */
paintEvent(QPaintEvent *)1139 void QX11EmbedContainer::paintEvent(QPaintEvent *)
1140 {
1141 }
1142 
1143 /*! \internal
1144 
1145     Returns whether or not the windows' embedded flag is set.
1146 */
isEmbedded() const1147 bool QX11EmbedContainerPrivate::isEmbedded() const
1148 {
1149     Q_Q(const QX11EmbedContainer);
1150     return ((QHackWidget *)q->window())->topData()->embedded == 1;
1151 }
1152 
1153 /*! \internal
1154 
1155     Returns the parentWinId of the window.
1156 */
topLevelParentWinId() const1157 WId QX11EmbedContainerPrivate::topLevelParentWinId() const
1158 {
1159     Q_Q(const QX11EmbedContainer);
1160     return ((QHackWidget *)q->window())->topData()->parentWinId;
1161 }
1162 
1163 /*!
1164     If the container has an embedded widget, this function returns
1165     the X11 window ID of the client; otherwise it returns 0.
1166 */
clientWinId() const1167 WId QX11EmbedContainer::clientWinId() const
1168 {
1169     Q_D(const QX11EmbedContainer);
1170     return d->client;
1171 }
1172 
1173 /*!
1174     Instructs the container to embed the X11 window with window ID \a
1175     id. The client widget will then move on top of the container
1176     window and be resized to fit into the container.
1177 
1178     The \a id should be the ID of a window controlled by an XEmbed
1179     enabled application, but this is not mandatory. If \a id does not
1180     belong to an XEmbed client widget, then focus handling,
1181     activation, accelerators and other features will not work
1182     properly.
1183 */
embedClient(WId id)1184 void QX11EmbedContainer::embedClient(WId id)
1185 {
1186     Q_D(QX11EmbedContainer);
1187 
1188     if (id == 0) {
1189 	d->emitError(InvalidWindowID);
1190 	return;
1191     }
1192 
1193     // Walk up the tree of parent windows to prevent embedding of ancestors.
1194     WId thisId = internalWinId();
1195     Window rootReturn;
1196     Window parentReturn;
1197     Window *childrenReturn = 0;
1198     unsigned int nchildrenReturn;
1199     do {
1200         if (XQueryTree(x11Info().display(), thisId, &rootReturn,
1201                        &parentReturn, &childrenReturn, &nchildrenReturn) == 0) {
1202 	    d->emitError(InvalidWindowID);
1203 	    return;
1204         }
1205         if (childrenReturn) {
1206             XFree(childrenReturn);
1207             childrenReturn = 0;
1208         }
1209 
1210         thisId = parentReturn;
1211         if (id == thisId) {
1212 	    d->emitError(InvalidWindowID);
1213 	    return;
1214         }
1215     } while (thisId != rootReturn);
1216 
1217     // watch for property notify events (see below)
1218     XGrabServer(x11Info().display());
1219     XWindowAttributes attrib;
1220     if (!XGetWindowAttributes(x11Info().display(), id, &attrib)) {
1221         XUngrabServer(x11Info().display());
1222 	d->emitError(InvalidWindowID);
1223 	return;
1224     }
1225     XSelectInput(x11Info().display(), id, attrib.your_event_mask | PropertyChangeMask | StructureNotifyMask);
1226     XUngrabServer(x11Info().display());
1227 
1228     // Put the window into WithdrawnState
1229     XUnmapWindow(x11Info().display(), id);
1230     XSync(x11Info().display(), False); // make sure the window is hidden
1231 
1232     /*
1233       Wait for notification from the window manager that the window is
1234       in withdrawn state.  According to the ICCCM section 4.1.3.1,
1235       we should wait for the WM_STATE property to either be deleted or
1236       set to WithdrawnState.
1237 
1238       For safety, we will not wait more than 500 ms, so that we can
1239       preemptively workaround buggy window managers.
1240     */
1241     QElapsedTimer t;
1242     t.start();
1243 
1244     functorData data;
1245     data.id = id;
1246     data.rootWindow = attrib.root;
1247     data.clearedWmState = false;
1248     data.reparentedToRoot = false;
1249 
1250     do {
1251 	if (t.elapsed() > 500) // time-out after 500 ms
1252 	    break;
1253 
1254 	XEvent event;
1255 	if (!XCheckIfEvent(x11Info().display(), &event, functor, (XPointer) &data)) {
1256 	    XSync(x11Info().display(), False);
1257             usleep(50000);
1258 	    continue;
1259 	}
1260 
1261         qApp->x11ProcessEvent(&event);
1262     } while (!data.clearedWmState || !data.reparentedToRoot);
1263 
1264     // restore the event mask
1265     XSelectInput(x11Info().display(), id, attrib.your_event_mask);
1266 
1267     switch (XReparentWindow(x11Info().display(), id, internalWinId(), 0, 0)) {
1268     case BadWindow:
1269     case BadMatch:
1270 	d->emitError(InvalidWindowID);
1271 	break;
1272     default:
1273 	break;
1274     }
1275 }
1276 
1277 /*! \internal
1278 
1279     Handles key, activation and focus events for the container.
1280 */
eventFilter(QObject * o,QEvent * event)1281 bool QX11EmbedContainer::eventFilter(QObject *o, QEvent *event)
1282 {
1283     Q_D(QX11EmbedContainer);
1284     switch (event->type()) {
1285     case QEvent::KeyPress:
1286         // Forward any keypresses to our client.
1287 	if (o == this && d->client) {
1288 	    lastKeyEvent.window = d->client;
1289 	    XSendEvent(x11Info().display(), d->client, false, KeyPressMask, (XEvent *) &lastKeyEvent);
1290 	    return true;
1291 	}
1292 	break;
1293     case QEvent::KeyRelease:
1294 	// Forward any keyreleases to our client.
1295 	if (o == this && d->client) {
1296 	    lastKeyEvent.window = d->client;
1297 	    XSendEvent(x11Info().display(), d->client, false, KeyReleaseMask, (XEvent *) &lastKeyEvent);
1298 	    return true;
1299 	}
1300 	break;
1301 
1302     case QEvent::WindowActivate:
1303 	// When our container window is activated, we pass the
1304 	// activation message on to our client. Note that X input
1305 	// focus is set to our focus proxy. We want to intercept all
1306 	// keypresses.
1307 	if (o == window() && d->client) {
1308             if (d->clientIsXEmbed) {
1309                 sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_ACTIVATE);
1310             } else {
1311                 d->checkGrab();
1312                 if (hasFocus())
1313                     XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time());
1314             }
1315             if (!d->isEmbedded())
1316                 d->moveInputToProxy();
1317 	}
1318 	break;
1319     case QEvent::WindowDeactivate:
1320 	// When our container window is deactivated, we pass the
1321 	// deactivation message to our client.
1322 	if (o == window() && d->client) {
1323 	    if (d->clientIsXEmbed)
1324 		sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_DEACTIVATE);
1325 	    else
1326 		d->checkGrab();
1327 	}
1328 	break;
1329     case QEvent::FocusIn:
1330         // When receiving FocusIn events generated by Tab or Backtab,
1331 	// we pass focus on to our client. Any mouse activity is sent
1332 	// directly to the client, and it will ask us for focus with
1333 	// XEMBED_REQUEST_FOCUS.
1334 	if (o == this && d->client) {
1335             if (!d->isEmbedded())
1336                 d->activeContainer = this;
1337 
1338             if (d->clientIsXEmbed) {
1339                 if (!d->isEmbedded())
1340                     d->moveInputToProxy();
1341 
1342 		QFocusEvent *fe = (QFocusEvent *)event;
1343 		switch (fe->reason()) {
1344 		case Qt::TabFocusReason:
1345 		    sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST);
1346 		    break;
1347 		case Qt::BacktabFocusReason:
1348 		    sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_LAST);
1349 		    break;
1350 		default:
1351 		    sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
1352 		    break;
1353 		}
1354 	    } else {
1355 		d->checkGrab();
1356                 XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time());
1357 	    }
1358 	}
1359 
1360 	break;
1361     case QEvent::FocusOut: {
1362 	// When receiving a FocusOut, we ask our client to remove its
1363 	// focus.
1364 	if (o == this && d->client) {
1365             if (!d->isEmbedded()) {
1366                 d->activeContainer = 0;
1367                 if (isActiveWindow())
1368                     d->moveInputToProxy();
1369             }
1370 
1371 	    if (d->clientIsXEmbed) {
1372 		QFocusEvent *fe = (QFocusEvent *)event;
1373 		if (o == this && d->client && fe->reason() != Qt::ActiveWindowFocusReason)
1374 		    sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_OUT);
1375 	    } else {
1376 		d->checkGrab();
1377 	    }
1378 	}
1379     }
1380 	break;
1381 
1382     case QEvent::Close: {
1383 	if (o == this && d->client) {
1384 	    // Unmap the client and reparent it to the root window.
1385 	    // Wait until the messages have been processed. Then ask
1386 	    // the window manager to delete the window.
1387 	    XUnmapWindow(x11Info().display(), d->client);
1388 	    XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(x11Info().screen()), 0, 0);
1389 	    XSync(x11Info().display(), false);
1390 
1391 	    XEvent ev;
1392 	    memset(&ev, 0, sizeof(ev));
1393 	    ev.xclient.type = ClientMessage;
1394 	    ev.xclient.window = d->client;
1395 	    ev.xclient.message_type = ATOM(WM_PROTOCOLS);
1396 	    ev.xclient.format = 32;
1397 	    ev.xclient.data.s[0] = ATOM(WM_DELETE_WINDOW);
1398 	    XSendEvent(x11Info().display(), d->client, false, NoEventMask, &ev);
1399 
1400 	    XFlush(x11Info().display());
1401 	    d->client = 0;
1402 	    d->clientIsXEmbed = false;
1403             d->wmMinimumSizeHint = QSize();
1404             updateGeometry();
1405             setEnabled(false);
1406 	    update();
1407 
1408 	    emit clientClosed();
1409 	}
1410     }
1411     default:
1412 	break;
1413     }
1414 
1415     return QWidget::eventFilter(o, event);
1416 }
1417 
1418 /*! \internal
1419 
1420     Handles X11 events for the container.
1421 */
x11Event(XEvent * event)1422 bool QX11EmbedContainer::x11Event(XEvent *event)
1423 {
1424     Q_D(QX11EmbedContainer);
1425 
1426     switch (event->type) {
1427     case CreateNotify:
1428 	// The client created an embedded window.
1429 	if (d->client)
1430 	    d->rejectClient(event->xcreatewindow.window);
1431 	else
1432 	    d->acceptClient(event->xcreatewindow.window);
1433       break;
1434     case DestroyNotify:
1435 	if (event->xdestroywindow.window == d->client) {
1436 	    // The client died.
1437 	    d->client = 0;
1438 	    d->clientIsXEmbed = false;
1439             d->wmMinimumSizeHint = QSize();
1440             updateGeometry();
1441 	    update();
1442             setEnabled(false);
1443 	    emit clientClosed();
1444 	}
1445         break;
1446     case ReparentNotify:
1447 	// The client sends us this if it reparents itself out of our
1448 	// widget.
1449 	if (event->xreparent.window == d->client && event->xreparent.parent != internalWinId()) {
1450 	    d->client = 0;
1451 	    d->clientIsXEmbed = false;
1452             d->wmMinimumSizeHint = QSize();
1453             updateGeometry();
1454 	    update();
1455             setEnabled(false);
1456 	    emit clientClosed();
1457 	} else if (event->xreparent.parent == internalWinId()) {
1458 	    // The client reparented itself into this window.
1459 	    if (d->client)
1460 		d->rejectClient(event->xreparent.window);
1461 	    else
1462 		d->acceptClient(event->xreparent.window);
1463 	}
1464 	break;
1465     case ClientMessage: {
1466 	if (event->xclient.message_type == ATOM(_XEMBED)) {
1467 	    // Ignore XEMBED messages not to ourselves
1468 	    if (event->xclient.window != internalWinId())
1469 		break;
1470 
1471 	    // Receiving an XEmbed message means the client
1472 	    // is an XEmbed client.
1473 	    d->clientIsXEmbed = true;
1474 
1475 	    Time msgtime = (Time) event->xclient.data.l[0];
1476 	    if (msgtime > X11->time)
1477 		X11->time = msgtime;
1478 
1479 	    switch (event->xclient.data.l[1]) {
1480 	    case XEMBED_REQUEST_FOCUS: {
1481 		// This typically happens when the client gets focus
1482 		// because of a mouse click.
1483 		if (!hasFocus())
1484 		    setFocus(Qt::OtherFocusReason);
1485 
1486 		// The message is passed along to the topmost container
1487 		// that eventually responds with a XEMBED_FOCUS_IN
1488 		// message. The focus in message is passed all the way
1489 		// back until it reaches the original focus
1490 		// requestor. In the end, not only the original client
1491 		// has focus, but also all its ancestor containers.
1492 		if (d->isEmbedded()) {
1493                     // If our window's embedded flag is set, then
1494 		    // that suggests that we are part of a client. The
1495 		    // parentWinId will then point to an container to whom
1496 		    // we must pass this message.
1497 		    sendXEmbedMessage(d->topLevelParentWinId(), x11Info().display(), XEMBED_REQUEST_FOCUS);
1498 		} else {
1499                     // Our window's embedded flag is not set,
1500 		    // so we are the topmost container. We respond to
1501 		    // the focus request message with a focus in
1502 		    // message. This message will pass on from client
1503 		    // to container to client until it reaches the
1504 		    // originator of the XEMBED_REQUEST_FOCUS message.
1505 		    sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
1506 		}
1507 
1508 		break;
1509 	    }
1510 	    case XEMBED_FOCUS_NEXT:
1511 		// Client sends this event when it received a tab
1512 		// forward and was at the end of its focus chain. If
1513 		// we are the only widget in the focus chain, we send
1514 		// ourselves a FocusIn event.
1515                 if (d->focus_next != this) {
1516 		    focusNextPrevChild(true);
1517                 } else {
1518                     QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason);
1519                     qApp->sendEvent(this, &event);
1520                 }
1521 
1522 		break;
1523 	    case XEMBED_FOCUS_PREV:
1524 		// Client sends this event when it received a backtab
1525 		// and was at the start of its focus chain. If we are
1526 		// the only widget in the focus chain, we send
1527 		// ourselves a FocusIn event.
1528                 if (d->focus_next != this) {
1529 		    focusNextPrevChild(false);
1530                 } else {
1531                     QFocusEvent event(QEvent::FocusIn, Qt::BacktabFocusReason);
1532                     qApp->sendEvent(this, &event);
1533                 }
1534 
1535 		break;
1536 	    default:
1537 		break;
1538 	    }
1539 	}
1540     }
1541 	break;
1542     case XButtonPress:
1543 	if (!d->clientIsXEmbed) {
1544             setFocus(Qt::MouseFocusReason);
1545             XAllowEvents(x11Info().display(), ReplayPointer, CurrentTime);
1546             return true;
1547 	}
1548 	break;
1549     case XButtonRelease:
1550 	if (!d->clientIsXEmbed)
1551             XAllowEvents(x11Info().display(), SyncPointer, CurrentTime);
1552 	break;
1553     default:
1554 	break;
1555     }
1556 
1557     return QWidget::x11Event(event);
1558 }
1559 
1560 /*! \internal
1561 
1562     Whenever the container is resized, we need to resize our client.
1563 */
resizeEvent(QResizeEvent *)1564 void QX11EmbedContainer::resizeEvent(QResizeEvent *)
1565 {
1566     Q_D(QX11EmbedContainer);
1567     if (d->client)
1568 	XResizeWindow(x11Info().display(), d->client, width(), height());
1569 }
1570 
1571 /*! \internal
1572 
1573     We use the QShowEvent to signal to our client that we want it to
1574     map itself. We do this by changing its window property
1575     XEMBED_INFO. The client will get an X11 PropertyNotify.
1576 */
showEvent(QShowEvent *)1577 void QX11EmbedContainer::showEvent(QShowEvent *)
1578 {
1579     Q_D(QX11EmbedContainer);
1580     if (d->client) {
1581         long data[] = {XEMBED_VERSION, XEMBED_MAPPED};
1582 	XChangeProperty(x11Info().display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32,
1583 			PropModeReplace, (unsigned char *) data, 2);
1584     }
1585 }
1586 
1587 /*! \internal
1588 
1589     We use the QHideEvent to signal to our client that we want it to
1590     unmap itself. We do this by changing its window property
1591     XEMBED_INFO. The client will get an X11 PropertyNotify.
1592 */
hideEvent(QHideEvent *)1593 void QX11EmbedContainer::hideEvent(QHideEvent *)
1594 {
1595     Q_D(QX11EmbedContainer);
1596     if (d->client) {
1597         long data[] = {XEMBED_VERSION, XEMBED_MAPPED};
1598 	XChangeProperty(x11Info().display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32,
1599 			PropModeReplace, (unsigned char *) data, 2);
1600     }
1601 }
1602 
1603 /*!
1604     \reimp
1605 */
event(QEvent * event)1606 bool QX11EmbedContainer::event(QEvent *event)
1607 {
1608     if (event->type() == QEvent::ParentChange) {
1609         XSelectInput(x11Info().display(), internalWinId(),
1610                      KeyPressMask | KeyReleaseMask
1611                      | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
1612                      | KeymapStateMask
1613                      | PointerMotionMask
1614                      | EnterWindowMask | LeaveWindowMask
1615                      | FocusChangeMask
1616                      | ExposureMask
1617                      | StructureNotifyMask
1618                      | SubstructureNotifyMask);
1619     }
1620     return QWidget::event(event);
1621 }
1622 
1623 /*! \internal
1624 
1625     Rejects a client window by reparenting it to the root window.  The
1626     client will receive a reparentnotify, and will most likely assume
1627     that the container has shut down. The XEmbed protocol does not
1628     define any way to reject a client window, but this is a clean way
1629     to do it.
1630 */
rejectClient(WId window)1631 void QX11EmbedContainerPrivate::rejectClient(WId window)
1632 {
1633     Q_Q(QX11EmbedContainer);
1634     q->setEnabled(false);
1635     XRemoveFromSaveSet(q->x11Info().display(), client);
1636     XReparentWindow(q->x11Info().display(), window, q->x11Info().appRootWindow(q->x11Info().screen()), 0, 0);
1637 }
1638 
1639 /*! \internal
1640 
1641     Accepts a client by mapping it, resizing it and optionally
1642     activating and giving it logical focusing through XEMBED messages.
1643 */
acceptClient(WId window)1644 void QX11EmbedContainerPrivate::acceptClient(WId window)
1645 {
1646     Q_Q(QX11EmbedContainer);
1647     client = window;
1648     q->setEnabled(true);
1649 
1650     // This tells Qt that we wish to forward DnD messages to
1651     // our client.
1652     if (!extra)
1653         createExtra();
1654     extraData()->xDndProxy = client;
1655 
1656     unsigned int version = XEmbedVersion();
1657 
1658     Atom actual_type_return;
1659     int actual_format_return;
1660     unsigned long nitems_return = 0;
1661     unsigned long bytes_after_return;
1662     unsigned char *prop_return = 0;
1663     unsigned int clientversion = 0;
1664 
1665     // Add this client to our saveset, so if we crash, the client window
1666     // doesn't get destroyed. This is useful for containers that restart
1667     // automatically after a crash, because it can simply reembed its clients
1668     // without having to restart them (KDE panel).
1669     XAddToSaveSet(q->x11Info().display(), client);
1670 
1671     // XEmbed clients have an _XEMBED_INFO property in which we can
1672     // fetch the version
1673     if (XGetWindowProperty(q->x11Info().display(), client, ATOM(_XEMBED_INFO), 0, 2, false,
1674                            ATOM(_XEMBED_INFO), &actual_type_return, &actual_format_return,
1675                            &nitems_return, &bytes_after_return, &prop_return) == Success) {
1676 
1677 	if (actual_type_return != None && actual_format_return != 0) {
1678 	    // Clients with the _XEMBED_INFO property are XEMBED clients.
1679 	    clientIsXEmbed = true;
1680 
1681 	    long *p = (long *)prop_return;
1682 	    if (nitems_return >= 2)
1683 		clientversion = (unsigned int)p[0];
1684 	}
1685 
1686 	XFree(prop_return);
1687     }
1688 
1689     // Store client window's original size and placement.
1690     Window root;
1691     int x_return, y_return;
1692     unsigned int width_return, height_return, border_width_return, depth_return;
1693     XGetGeometry(q->x11Info().display(), client, &root, &x_return, &y_return,
1694 		 &width_return, &height_return, &border_width_return, &depth_return);
1695     clientOriginalRect.setCoords(x_return, y_return,
1696 				 x_return + width_return - 1,
1697 				 y_return + height_return - 1);
1698 
1699     // Ask the client for its minimum size.
1700     XSizeHints size;
1701     long msize;
1702     if (XGetWMNormalHints(q->x11Info().display(), client, &size, &msize) && (size.flags & PMinSize)) {
1703 	wmMinimumSizeHint = QSize(size.min_width, size.min_height);
1704         q->updateGeometry();
1705     }
1706 
1707     // The container should set the data2 field to the lowest of its
1708     // supported version number and that of the client (from
1709     // _XEMBED_INFO property).
1710     unsigned int minversion = version > clientversion ? clientversion : version;
1711     sendXEmbedMessage(client, q->x11Info().display(), XEMBED_EMBEDDED_NOTIFY, q->internalWinId(), minversion);
1712     XMapWindow(q->x11Info().display(), client);
1713 
1714     // Resize it, but no smaller than its minimum size hint.
1715     XResizeWindow(q->x11Info().display(),
1716                   client,
1717                   qMax(q->width(), wmMinimumSizeHint.width()),
1718                   qMax(q->height(), wmMinimumSizeHint.height()));
1719     q->update();
1720 
1721     // Not mentioned in the protocol is that if the container
1722     // is already active, the client must be activated to work
1723     // properly.
1724     if (q->window()->isActiveWindow())
1725 	sendXEmbedMessage(client, q->x11Info().display(), XEMBED_WINDOW_ACTIVATE);
1726 
1727     // Also, if the container already has focus, then it must
1728     // send a focus in message to its new client; otherwise we ask
1729     // it to remove focus.
1730     if (q->focusWidget() == q && q->hasFocus())
1731 	sendXEmbedMessage(client, q->x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST);
1732     else
1733 	sendXEmbedMessage(client, q->x11Info().display(), XEMBED_FOCUS_OUT);
1734 
1735     if (!clientIsXEmbed) {
1736         checkGrab();
1737         if (q->hasFocus()) {
1738             XSetInputFocus(q->x11Info().display(), client, XRevertToParent, x11Time());
1739         }
1740     } else {
1741         if (!isEmbedded())
1742             moveInputToProxy();
1743     }
1744 
1745     emit q->clientIsEmbedded();
1746 }
1747 
1748 /*! \internal
1749 
1750     Moves X11 keyboard input focus to the focusProxy, unless the focus
1751     is there already. When X11 keyboard input focus is on the
1752     focusProxy, which is a child of the container and a sibling of the
1753     client, X11 keypresses and keyreleases will always go to the proxy
1754     and not to the client.
1755 */
moveInputToProxy()1756 void QX11EmbedContainerPrivate::moveInputToProxy()
1757 {
1758     Q_Q(QX11EmbedContainer);
1759     // Following Owen Taylor's advice from the XEmbed specification to
1760     // always use CurrentTime when no explicit user action is involved.
1761     XSetInputFocus(q->x11Info().display(), focusProxy->internalWinId(), XRevertToParent, CurrentTime);
1762 }
1763 
1764 /*! \internal
1765 
1766     Ask the window manager to give us a default minimum size.
1767 */
minimumSizeHint() const1768 QSize QX11EmbedContainer::minimumSizeHint() const
1769 {
1770     Q_D(const QX11EmbedContainer);
1771     if (!d->client || !d->wmMinimumSizeHint.isValid())
1772 	return QWidget::minimumSizeHint();
1773     return d->wmMinimumSizeHint;
1774 }
1775 
1776 /*! \internal
1777 
1778 */
checkGrab()1779 void QX11EmbedContainerPrivate::checkGrab()
1780 {
1781     Q_Q(QX11EmbedContainer);
1782     if (!clientIsXEmbed && q->isActiveWindow() && !q->hasFocus()) {
1783         if (!xgrab) {
1784             XGrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId(),
1785                         true, ButtonPressMask, GrabModeSync, GrabModeAsync,
1786                         None, None);
1787         }
1788         xgrab = true;
1789     } else {
1790 	if (xgrab)
1791 	    XUngrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId());
1792         xgrab = false;
1793     }
1794 }
1795 
1796 /*!
1797     Detaches the client from the embedder. The client will appear as a
1798     standalone window on the desktop.
1799 */
discardClient()1800 void QX11EmbedContainer::discardClient()
1801 {
1802     Q_D(QX11EmbedContainer);
1803     if (d->client) {
1804 	XResizeWindow(x11Info().display(), d->client, d->clientOriginalRect.width(),
1805 		      d->clientOriginalRect.height());
1806 
1807 	d->rejectClient(d->client);
1808     }
1809 }
1810 
1811 QT_END_NAMESPACE
1812