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