source: trunk/src/gui/kernel/qx11embed_x11.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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