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

Last change on this file since 439 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 56.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qx11embed_x11.h"
43#include <qapplication.h>
44#include <qevent.h>
45#include <qpainter.h>
46#include <qlayout.h>
47#include <qstyle.h>
48#include <qstyleoption.h>
49#include <qdatetime.h>
50#include <qpointer.h>
51#include <qdebug.h>
52#include <qx11info_x11.h>
53#include <private/qt_x11_p.h>
54#include <private/qwidget_p.h>
55
56#define XK_MISCELLANY
57#define XK_LATIN1
58#define None 0
59#include <X11/Xlib.h>
60#include <X11/Xatom.h>
61#include <X11/Xutil.h>
62#include <X11/keysymdef.h>
63#include <X11/X.h>
64
65#ifndef XK_ISO_Left_Tab
66#define XK_ISO_Left_Tab 0xFE20
67#endif
68
69//#define QX11EMBED_DEBUG
70#ifdef QX11EMBED_DEBUG
71#include <qdebug.h>
72#endif
73
74QT_BEGIN_NAMESPACE
75
76/*!
77 \class QX11EmbedWidget
78 \ingroup advanced
79
80 \brief The QX11EmbedWidget class provides an XEmbed client widget.
81
82 XEmbed is an X11 protocol that supports the embedding of a widget
83 from one application into another application.
84
85 An XEmbed \e{client widget} is a window that is embedded into a
86 \e container. A container is the graphical location that embeds
87 (or \e swallows) an external application.
88
89 QX11EmbedWidget is a widget used for writing XEmbed applets or
90 plugins. When it has been embedded and the container receives tab
91 focus, focus is passed on to the widget. When the widget reaches
92 the end of its focus chain, focus is passed back to the
93 container. Window activation, accelerator support, modality and
94 drag and drop (XDND) are also handled.
95
96 The widget and container can both initiate the embedding. If the
97 widget is the initiator, the X11 window ID of the container that
98 it wants to embed itself into must be passed to embedInto().
99
100 If the container initiates the embedding, the window ID of the
101 embedded widget must be known. The container calls embed(),
102 passing the window ID.
103
104 This example shows an application that embeds a QX11EmbedWidget
105 subclass into the window whose ID is passed as a command-line
106 argument:
107
108 \snippet doc/src/snippets/qx11embedwidget/main.cpp 0
109
110 The problem of obtaining the window IDs is often solved by the
111 container invoking the application that provides the widget as a
112 separate process (as a panel invokes a docked applet), passing
113 its window ID to the new process as a command-line argument. The
114 new process can then call embedInto() with the container's window
115 ID, as shown in the example code above. Similarly, the new
116 process can report its window ID to the container through IPC, in
117 which case the container can embed the widget.
118
119 When the widget has been embedded, it emits the signal
120 embedded(). If it is closed by the container, the widget emits
121 containerClosed(). If an error occurs when embedding, error() is
122 emitted.
123
124 There are XEmbed widgets available for KDE and GTK+. The GTK+
125 equivalent of QX11EmbedWidget is GtkPlug. The KDE widget is called
126 QXEmbed.
127
128 \sa QX11EmbedContainer, {XEmbed Specification}
129*/
130
131/*!
132 \class QX11EmbedContainer
133 \ingroup advanced
134
135 \brief The QX11EmbedContainer class provides an XEmbed container
136 widget.
137
138 XEmbed is an X11 protocol that supports the embedding of a widget
139 from one application into another application.
140
141 An XEmbed \e container is the graphical location that embeds an
142 external \e {client widget}. A client widget is a window that is
143 embedded into a container.
144
145 When a widget has been embedded and the container receives tab
146 focus, focus is passed on to the widget. When the widget reaches
147 the end of its focus chain, focus is passed back to the
148 container. Window activation, accelerator support, modality and
149 drag and drop (XDND) are also handled.
150
151 QX11EmbedContainer is commonly used for writing panels or
152 toolbars that hold applets, or for \e swallowing X11
153 applications. When writing a panel application, one container
154 widget is created on the toolbar, and it can then either swallow
155 another widget using embed(), or allow an XEmbed widget to be
156 embedded into itself. The container's X11 window ID, which is
157 retrieved with winId(), must then be known to the client widget.
158 After embedding, the client's window ID can be retrieved with
159 clientWinId().
160
161 In the following example, a container widget is created as the
162 main widget. It then invokes an application called "playmovie",
163 passing its window ID as a command line argument. The "playmovie"
164 program is an XEmbed client widget. The widget embeds itself into
165 the container using the container's window ID.
166
167 \snippet doc/src/snippets/qx11embedcontainer/main.cpp 0
168
169 When the client widget is embedded, the container emits the
170 signal clientIsEmbedded(). The signal clientClosed() is emitted
171 when a widget is closed.
172
173 It is possible for QX11EmbedContainer to embed XEmbed widgets
174 from toolkits other than Qt, such as GTK+. Arbitrary (non-XEmbed)
175 X11 widgets can also be embedded, but the XEmbed-specific
176 features such as window activation and focus handling are then
177 lost.
178
179 The GTK+ equivalent of QX11EmbedContainer is GtkSocket. The KDE
180 widget is called QXEmbed.
181
182 \sa QX11EmbedWidget, {XEmbed Specification}
183*/
184
185/*! \fn void QX11EmbedWidget::embedded()
186
187 This signal is emitted by the widget that has been embedded by an
188 XEmbed container.
189*/
190
191/*! \fn void QX11EmbedWidget::containerClosed()
192
193 This signal is emitted by the client widget when the container
194 closes the widget. This can happen if the container itself
195 closes, or if the widget is rejected.
196
197 The container can reject a widget for any reason, but the most
198 common cause of a rejection is when an attempt is made to embed a
199 widget into a container that already has an embedded widget.
200*/
201
202/*! \fn void QX11EmbedContainer::clientIsEmbedded()
203
204 This signal is emitted by the container when a client widget has
205 been embedded.
206*/
207
208/*! \fn void QX11EmbedContainer::clientClosed()
209
210 This signal is emitted by the container when the client widget
211 closes.
212*/
213
214/*!
215 \fn void QX11EmbedWidget::error(QX11EmbedWidget::Error error)
216
217 This signal is emitted if an error occurred as a result of
218 embedding into or communicating with a container. The specified
219 \a error describes the problem that occurred.
220
221 \sa QX11EmbedWidget::Error
222*/
223
224/*!
225 \fn QX11EmbedContainer::Error QX11EmbedContainer::error() const
226
227 Returns the last error that occurred.
228*/
229
230/*! \fn void QX11EmbedContainer::error(QX11EmbedContainer::Error error)
231
232 This signal is emitted if an error occurred when embedding or
233 communicating with a client. The specified \a error describes the
234 problem that occurred.
235
236 \sa QX11EmbedContainer::Error
237*/
238
239/*!
240 \enum QX11EmbedWidget::Error
241
242 \value Unknown An unrecognized error occurred.
243
244 \value InvalidWindowID The X11 window ID of the container was
245 invalid. This error is usually triggered by passing an invalid
246 window ID to embedInto().
247
248 \omitvalue Internal
249*/
250
251/*!
252 \enum QX11EmbedContainer::Error
253
254 \value Unknown An unrecognized error occurred.
255
256 \value InvalidWindowID The X11 window ID of the container was
257 invalid. This error is usually triggered by passing an invalid
258 window ID to embed().
259
260 \omitvalue Internal
261*/
262
263const int XButtonPress = ButtonPress;
264const int XButtonRelease = ButtonRelease;
265#undef ButtonPress
266#undef ButtonRelease
267
268// This is a hack to move topData() out from QWidgetPrivate to public. We
269// need to to inspect window()'s embedded state.
270class QHackWidget : public QWidget
271{
272 Q_DECLARE_PRIVATE(QWidget)
273public:
274 QTLWExtra* topData() { return d_func()->topData(); }
275};
276
277static unsigned int XEMBED_VERSION = 0;
278
279enum QX11EmbedMessageType {
280 XEMBED_EMBEDDED_NOTIFY = 0,
281 XEMBED_WINDOW_ACTIVATE = 1,
282 XEMBED_WINDOW_DEACTIVATE = 2,
283 XEMBED_REQUEST_FOCUS = 3,
284 XEMBED_FOCUS_IN = 4,
285 XEMBED_FOCUS_OUT = 5,
286 XEMBED_FOCUS_NEXT = 6,
287 XEMBED_FOCUS_PREV = 7,
288 XEMBED_MODALITY_ON = 10,
289 XEMBED_MODALITY_OFF = 11,
290 XEMBED_REGISTER_ACCELERATOR = 12,
291 XEMBED_UNREGISTER_ACCELERATOR = 13,
292 XEMBED_ACTIVATE_ACCELERATOR = 14
293};
294
295enum QX11EmbedFocusInDetail {
296 XEMBED_FOCUS_CURRENT = 0,
297 XEMBED_FOCUS_FIRST = 1,
298 XEMBED_FOCUS_LAST = 2
299};
300
301enum QX11EmbedFocusInFlags {
302 XEMBED_FOCUS_OTHER = (0 << 0),
303 XEMBED_FOCUS_WRAPAROUND = (1 << 0)
304};
305
306enum QX11EmbedInfoFlags {
307 XEMBED_MAPPED = (1 << 0)
308};
309
310enum QX11EmbedAccelModifiers {
311 XEMBED_MODIFIER_SHIFT = (1 << 0),
312 XEMBED_MODIFIER_CONTROL = (1 << 1),
313 XEMBED_MODIFIER_ALT = (1 << 2),
314 XEMBED_MODIFIER_SUPER = (1 << 3),
315 XEMBED_MODIFIER_HYPER = (1 << 4)
316};
317
318enum QX11EmbedAccelFlags {
319 XEMBED_ACCELERATOR_OVERLOADED = (1 << 0)
320};
321
322// Silence the default X11 error handler.
323static int x11ErrorHandler(Display *, XErrorEvent *)
324{
325 return 0;
326}
327
328// Returns the X11 timestamp. Maintained mainly by qapplication
329// internals, but also updated by the XEmbed widgets.
330static Time x11Time()
331{
332 return qt_x11Data->time;
333}
334
335// Gives the version and flags of the supported XEmbed protocol.
336static unsigned int XEmbedVersion()
337{
338 return 0;
339}
340
341// Sends an XEmbed message.
342static void sendXEmbedMessage(WId window, Display *display, long message,
343 long detail = 0, long data1 = 0, long data2 = 0)
344{
345 XClientMessageEvent c;
346 memset(&c, 0, sizeof(c));
347 c.type = ClientMessage;
348 c.message_type = ATOM(_XEMBED);
349 c.format = 32;
350 c.display = display;
351 c.window = window;
352
353 c.data.l[0] = x11Time();
354 c.data.l[1] = message;
355 c.data.l[2] = detail;
356 c.data.l[3] = data1;
357 c.data.l[4] = data2;
358
359 XSendEvent(display, window, false, NoEventMask, (XEvent *) &c);
360}
361
362// From qapplication_x11.cpp
363static XKeyEvent lastKeyEvent;
364
365static QCoreApplication::EventFilter oldX11EventFilter;
366
367// The purpose of this global x11 filter is for one to capture the key
368// events in their original state, but most importantly this is the
369// only way to get the WM_TAKE_FOCUS message from WM_PROTOCOLS.
370static bool x11EventFilter(void *message, long *result)
371{
372 XEvent *event = reinterpret_cast<XEvent *>(message);
373 if (event->type == XKeyPress || event->type == XKeyRelease)
374 lastKeyEvent = event->xkey;
375
376 if (oldX11EventFilter && oldX11EventFilter != &x11EventFilter)
377 return oldX11EventFilter(message, result);
378 else
379 return false;
380}
381
382//
383struct functorData
384{
385 Window id, rootWindow;
386 bool clearedWmState;
387 bool reparentedToRoot;
388};
389
390static Bool functor(Display *display, XEvent *event, XPointer arg)
391{
392 functorData *data = (functorData *) arg;
393
394 if (!data->reparentedToRoot && event->type == ReparentNotify
395 && event->xreparent.window == data->id
396 && event->xreparent.parent == data->rootWindow) {
397 data->reparentedToRoot = true;
398 return true;
399 }
400
401 if (!data->clearedWmState
402 && event->type == PropertyNotify
403 && event->xproperty.window == data->id
404 && event->xproperty.atom == ATOM(WM_STATE)) {
405 if (event->xproperty.state == PropertyDelete) {
406 data->clearedWmState = true;
407 return true;
408 }
409
410 Atom ret;
411 int format, status;
412 unsigned char *retval;
413 unsigned long nitems, after;
414 status = XGetWindowProperty(display, data->id, ATOM(WM_STATE), 0, 2, False, ATOM(WM_STATE),
415 &ret, &format, &nitems, &after, &retval );
416 if (status == Success && ret == ATOM(WM_STATE) && format == 32 && nitems > 0) {
417 long *state = (long *)retval;
418 if (state[0] == WithdrawnState) {
419 data->clearedWmState = true;
420 return true;
421 }
422 }
423 }
424
425 return false;
426}
427
428class QX11EmbedWidgetPrivate : public QWidgetPrivate
429{
430 Q_DECLARE_PUBLIC(QX11EmbedWidget)
431public:
432 inline QX11EmbedWidgetPrivate()
433 {
434 lastError = QX11EmbedWidget::Unknown;
435 container = 0;
436 }
437
438 void setEmbedded();
439
440 void emitError(QX11EmbedWidget::Error error) {
441 Q_Q(QX11EmbedWidget);
442
443 lastError = error;
444 emit q->error(error);
445 }
446
447 enum FocusWidgets {
448 FirstFocusWidget,
449 LastFocusWidget
450 };
451
452 int focusOriginator;
453 QWidget *getFocusWidget(FocusWidgets fw);
454 void checkActivateWindow(QObject *o);
455 QX11EmbedWidget *xEmbedWidget(QObject *o) const;
456 void clearFocus();
457
458 WId container;
459 QPointer<QWidget> currentFocus;
460
461 QX11EmbedWidget::Error lastError;
462
463};
464
465/*!
466 Constructs a QX11EmbedWidget object with the given \a parent.
467*/
468QX11EmbedWidget::QX11EmbedWidget(QWidget *parent)
469 : QWidget(*new QX11EmbedWidgetPrivate, parent, 0)
470{
471 XSetErrorHandler(x11ErrorHandler);
472
473 setAttribute(Qt::WA_NativeWindow);
474 setAttribute(Qt::WA_DontCreateNativeAncestors);
475 createWinId();
476 XSelectInput(x11Info().display(), internalWinId(),
477 KeyPressMask | KeyReleaseMask | ButtonPressMask
478 | ButtonReleaseMask
479 | KeymapStateMask | ButtonMotionMask | PointerMotionMask
480 | FocusChangeMask
481 | ExposureMask | StructureNotifyMask
482 | SubstructureNotifyMask | PropertyChangeMask);
483
484 long data[] = {XEMBED_VERSION, XEMBED_MAPPED};
485 XChangeProperty(x11Info().display(), internalWinId(), ATOM(_XEMBED_INFO),
486 ATOM(_XEMBED_INFO), 32, PropModeReplace,
487 (unsigned char*) data, 2);
488
489 setFocusPolicy(Qt::StrongFocus);
490 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
491 QApplication::instance()->installEventFilter(this);
492
493#ifdef QX11EMBED_DEBUG
494 qDebug() << "QX11EmbedWidget::QX11EmbedWidget: constructed client"
495 << (void *)this << "with winId" << winId();
496#endif
497}
498
499/*!
500 Destructs the QX11EmbedWidget object. If the widget is embedded
501 when deleted, it is hidden and then detached from its container,
502 so that the container is free to embed a new widget.
503*/
504QX11EmbedWidget::~QX11EmbedWidget()
505{
506 Q_D(QX11EmbedWidget);
507 if (d->container) {
508#ifdef QX11EMBED_DEBUG
509 qDebug() << "QX11EmbedWidget::~QX11EmbedWidget: unmapping"
510 << (void *)this << "with winId" << winId()
511 << "from container with winId" << d->container;
512#endif
513 XUnmapWindow(x11Info().display(), internalWinId());
514 XReparentWindow(x11Info().display(), internalWinId(), x11Info().appRootWindow(), 0, 0);
515 }
516
517#ifdef QX11EMBED_DEBUG
518 qDebug() << "QX11EmbedWidget::~QX11EmbedWidget: destructed client"
519 << (void *)this << "with winId" << winId();
520#endif
521}
522
523/*!
524 Returns the type of error that occurred last. This is the same error code
525 that is emitted by the error() signal.
526
527 \sa Error
528*/
529QX11EmbedWidget::Error QX11EmbedWidget::error() const
530{
531 return d_func()->lastError;
532}
533
534/*!
535 When this function is called, the widget embeds itself into the
536 container whose window ID is \a id.
537
538 If \a id is \e not the window ID of a container this function will
539 behave unpredictably.
540*/
541void QX11EmbedWidget::embedInto(WId id)
542{
543 Q_D(QX11EmbedWidget);
544#ifdef QX11EMBED_DEBUG
545 qDebug() << "QX11EmbedWidget::embedInto: embedding client"
546 << (void *)this << "with winId" << winId() << "into container"
547 << id;
548#endif
549
550 d->container = id;
551 switch (XReparentWindow(x11Info().display(), internalWinId(), d->container, 0, 0)) {
552 case BadWindow:
553 d->emitError(InvalidWindowID);
554 break;
555 case BadMatch:
556 d->emitError(Internal);
557 break;
558 case Success:
559 default:
560 break;
561 }
562 QTLWExtra* x = d->extra ? d->extra->topextra : 0;
563 if (x)
564 x->frameStrut.setCoords(0, 0, 0, 0);
565 d->data.fstrut_dirty = false;
566}
567
568/*! \internal
569
570 Gets the first or last child widget that can get focus.
571*/
572QWidget *QX11EmbedWidgetPrivate::getFocusWidget(FocusWidgets fw)
573{
574 Q_Q(QX11EmbedWidget);
575 QWidget *tlw = q;
576 QWidget *w = tlw->nextInFocusChain();
577
578 QWidget *last = tlw;
579
580 extern bool qt_tab_all_widgets;
581 uint focus_flag = qt_tab_all_widgets ? Qt::TabFocus : Qt::StrongFocus;
582
583 while (w != tlw)
584 {
585 if (((w->focusPolicy() & focus_flag) == focus_flag)
586 && w->isVisibleTo(q) && w->isEnabled())
587 {
588 last = w;
589 if (fw == FirstFocusWidget)
590 break;
591 }
592 w = w->nextInFocusChain();
593 }
594
595 return last;
596}
597
598/*! \internal
599
600 Returns the xembed widget that the widget is a child of
601*/
602QX11EmbedWidget *QX11EmbedWidgetPrivate::xEmbedWidget(QObject *o) const
603{
604 QX11EmbedWidget *xec = 0;
605
606 // Check the widget itself, then its parents, and find the first
607 // QX11EmbedWidget.
608 do {
609 if ((xec = qobject_cast<QX11EmbedWidget *>(o)))
610 return xec;
611 } while ((o = o->parent()));
612 return 0;
613}
614
615/*! \internal
616
617 Checks the active window.
618*/
619void QX11EmbedWidgetPrivate::checkActivateWindow(QObject *o)
620{
621 Q_Q(QX11EmbedWidget);
622 QX11EmbedWidget *xec = xEmbedWidget(o);
623
624 // check if we are in the right xembed client
625 if (q != xec)
626 return;
627
628 QWidget *w = qobject_cast<QWidget *>(o);
629
630 // if it is no active window, then don't do the change
631 if (!(w && qApp->activeWindow()))
632 return;
633
634 // if it already is the active window, don't do anything
635 if (w->window() != qApp->activeWindow())
636 {
637 qApp->setActiveWindow(w->window());
638 currentFocus = w;
639
640 sendXEmbedMessage(xec->containerWinId(), q->x11Info().display(), XEMBED_REQUEST_FOCUS);
641 }
642}
643
644/*! \internal
645
646 Clears the focus
647*/
648void QX11EmbedWidgetPrivate::clearFocus()
649{
650 Q_Q(QX11EmbedWidget);
651 // Setting focus on the client itself removes Qt's
652 // logical focus rectangle. We can't just do a
653 // clearFocus here, because when we send the synthetic
654 // FocusIn to ourselves on activation, Qt will set
655 // focus on focusWidget() again. This way, we "hide"
656 // focus rather than clearing it.
657
658 if (!q->window()->hasFocus())
659 q->window()->setFocus(Qt::OtherFocusReason);
660
661 currentFocus = 0;
662}
663
664/*! \internal
665
666 Sets the embedded flag on the client.
667*/
668void QX11EmbedWidgetPrivate::setEmbedded()
669{
670 Q_Q(QX11EmbedWidget);
671 ((QHackWidget *)q->window())->topData()->embedded = 1;
672}
673
674/*! \internal
675
676 Handles WindowActivate and FocusIn events for the client.
677*/
678bool QX11EmbedWidget::eventFilter(QObject *o, QEvent *event)
679{
680 Q_D(QX11EmbedWidget);
681 switch (event->type()) {
682 case QEvent::FocusIn:
683 switch (((QFocusEvent *)event)->reason()) {
684 case Qt::MouseFocusReason:
685 // If the user clicks into one of the client widget's
686 // children and we didn't have focus already, we request
687 // focus from our container.
688 if (d->xEmbedWidget(o) == this) {
689 if (d->currentFocus.isNull())
690 sendXEmbedMessage(d->container, x11Info().display(), XEMBED_REQUEST_FOCUS);
691
692 d->currentFocus = qobject_cast<QWidget *>(o);
693 }
694 break;
695 case Qt::TabFocusReason:
696 // If the xembed client receives a focus event because of
697 // a Tab, then we are at the end of our focus chain and we
698 // ask the container to move to its next focus widget.
699 if (o == this) {
700 d->clearFocus();
701 sendXEmbedMessage(d->container, x11Info().display(), XEMBED_FOCUS_NEXT);
702 return true;
703 } else {
704 // We're listening on events from qApp, so in order
705 // for us to know who to set focus on if we receive an
706 // activation event, we note the widget that got the
707 // focusin last.
708 if (d->xEmbedWidget(o) == this)
709 d->currentFocus = qobject_cast<QWidget *>(o);
710 }
711 break;
712 case Qt::BacktabFocusReason:
713 // If the window receives a focus event because of
714 // a Backtab, then we are at the start of our focus chain
715 // and we ask the container to move to its previous focus
716 // widget.
717 if (o == this) {
718 // See comment for Tab.
719 // If we receive a XEMBED_FOCUS_IN
720 // XEMBED_FOCUS_CURRENT, we will set focus in
721 // currentFocus. To avoid that in this case, we reset
722 // currentFocus.
723 d->clearFocus();
724 sendXEmbedMessage(d->container, x11Info().display(), XEMBED_FOCUS_PREV);
725 return true;
726 } else {
727 if (d->xEmbedWidget(o) == this)
728 d->currentFocus = qobject_cast<QWidget *>(o);
729 }
730 break;
731 case Qt::ActiveWindowFocusReason:
732 if (isActiveWindow()) {
733 if (!d->currentFocus.isNull()) {
734 if (!d->currentFocus->hasFocus())
735 d->currentFocus->setFocus(Qt::OtherFocusReason);
736 } else {
737 d->clearFocus();
738 return true;
739 }
740 }
741
742 break;
743 case Qt::PopupFocusReason:
744 case Qt::ShortcutFocusReason:
745 case Qt::OtherFocusReason:
746 // If focus is received to any child widget because of any
747 // other reason, remember the widget so that we can give
748 // it focus again if we're activated.
749 if (d->xEmbedWidget(o) == this) {
750 d->currentFocus = qobject_cast<QWidget *>(o);
751 }
752 break;
753 default:
754 break;
755 }
756 break;
757 case QEvent::MouseButtonPress:
758 // If we get a mouse button press event inside a embedded widget
759 // make sure this is the active window in qapp.
760 d->checkActivateWindow(o);
761 break;
762 default:
763 break;
764 }
765
766 return QWidget::eventFilter(o, event);
767}
768
769/*! \internal
770
771 Handles some notification events and client messages. Client side
772 XEmbed message receiving is also handled here.
773*/
774bool QX11EmbedWidget::x11Event(XEvent *event)
775{
776 Q_D(QX11EmbedWidget);
777 switch (event->type) {
778 case DestroyNotify:
779#ifdef QX11EMBED_DEBUG
780 qDebug() << "QX11EmbedWidget::x11Event: client"
781 << (void *)this << "with winId" << winId()
782 << "received a DestroyNotify";
783#endif
784 // If the container window is destroyed, we signal this to the user.
785 d->container = 0;
786 emit containerClosed();
787 break;
788 case ReparentNotify:
789#ifdef QX11EMBED_DEBUG
790 qDebug() << "QX11EmbedWidget::x11Event: client"
791 << (void *)this << "with winId" << winId()
792 << "received a ReparentNotify to"
793 << ((event->xreparent.parent == x11Info().appRootWindow())
794 ? QString::fromLatin1("root") : QString::number(event->xreparent.parent));
795#endif
796 // If the container shuts down, we will be reparented to the
797 // root window. We must also consider the case that we may be
798 // reparented from one container to another.
799 if (event->xreparent.parent == x11Info().appRootWindow()) {
800 if (((QHackWidget *)this)->topData()->embedded) {
801 d->container = 0;
802 emit containerClosed();
803 }
804 return true;
805 } else {
806 d->container = event->xreparent.parent;
807 }
808 break;
809 case UnmapNotify:
810 // Mapping and unmapping are handled by changes to the
811 // _XEMBED_INFO property. Any default map/unmap requests are
812 // ignored.
813 return true;
814 case PropertyNotify:
815 // The container sends us map/unmap messages through the
816 // _XEMBED_INFO property. We adhere to the XEMBED_MAPPED bit in
817 // data2.
818 if (event->xproperty.atom == ATOM(_XEMBED_INFO)) {
819 Atom actual_type_return;
820 int actual_format_return;
821 unsigned long nitems_return;
822 unsigned long bytes_after_return;
823 unsigned char *prop_return = 0;
824 if (XGetWindowProperty(x11Info().display(), internalWinId(), ATOM(_XEMBED_INFO), 0, 2,
825 false, ATOM(_XEMBED_INFO), &actual_type_return,
826 &actual_format_return, &nitems_return,
827 &bytes_after_return, &prop_return) == Success) {
828 if (nitems_return > 1) {
829 if (((int * )prop_return)[1] & XEMBED_MAPPED) {
830 XMapWindow(x11Info().display(), internalWinId());
831 } else {
832 XUnmapWindow(x11Info().display(), internalWinId());
833 }
834 }
835 }
836 }
837
838 break;
839 case ClientMessage:
840 // XEMBED messages have message_type _XEMBED
841 if (event->xclient.message_type == ATOM(_XEMBED)) {
842 // Discard XEMBED messages not to ourselves. (### dead code?)
843 if (event->xclient.window != internalWinId())
844 break;
845
846 // Update qt_x_time if necessary
847 Time msgtime = (Time) event->xclient.data.l[0];
848 if (msgtime > X11->time)
849 X11->time = msgtime;
850
851 switch (event->xclient.data.l[1]) {
852 case XEMBED_WINDOW_ACTIVATE: {
853 // When we receive an XEMBED_WINDOW_ACTIVATE, we simply send
854 // ourselves a WindowActivate event. Real activation happens
855 // when receive XEMBED_FOCUS_IN.
856 if (!isActiveWindow()) {
857 QEvent ev(QEvent::WindowActivate);
858 QApplication::sendEvent(this, &ev);
859 }
860 }
861 break;
862 case XEMBED_WINDOW_DEACTIVATE: {
863 // When we receive an XEMBED_WINDOW_DEACTIVATE, we simply send
864 // ourselves a WindowDeactivate event. Real activation happens
865 // when receive XEMBED_FOCUS_IN.
866 if (isActiveWindow()) {
867 if (!qApp->activePopupWidget())
868 QApplication::setActiveWindow(0);
869 } else {
870 QEvent ev(QEvent::WindowDeactivate);
871 QApplication::sendEvent(this, &ev);
872 }
873 }
874 break;
875 case XEMBED_EMBEDDED_NOTIFY: {
876#ifdef QX11EMBED_DEBUG
877 qDebug() << "QX11EmbedWidget::x11Event: client"
878 << (void *)this << "with winId" << winId()
879 << "received an XEMBED EMBEDDED NOTIFY message";
880#endif
881 // In this message's l[2] we have the max version
882 // supported by both the client and the
883 // container. QX11EmbedWidget does not use this field.
884
885 // We have been embedded, so we set our
886 // client's embedded flag.
887 d->setEmbedded();
888 emit embedded();
889 }
890 break;
891 case XEMBED_FOCUS_IN:
892 // don't set the focus if a modal dialog is open
893 if (qApp->activeModalWidget())
894 break;
895
896 // in case we embed more than one topLevel window inside the same
897 // host window.
898 if (window() != qApp->activeWindow())
899 qApp->setActiveWindow(this);
900
901 switch (event->xclient.data.l[2]) {
902 case XEMBED_FOCUS_CURRENT:
903 // The container sends us this message if it wants
904 // us to focus on the widget that last had focus.
905 // This is the reply when XEMBED_REQUEST_FOCUS is
906 // sent to the container.
907 if (!d->currentFocus.isNull()) {
908 if (!d->currentFocus->hasFocus())
909 d->currentFocus->setFocus(Qt::OtherFocusReason);
910 } else {
911 // No widget currently has focus. We set focus
912 // on the first widget next to the
913 // client widget. Since the setFocus will not work
914 // if the window is disabled, set the currentFocus
915 // directly so that it's set on window activate.
916 d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::FirstFocusWidget);
917 d->currentFocus->setFocus(Qt::OtherFocusReason);
918 }
919 break;
920 case XEMBED_FOCUS_FIRST:
921 // The container sends this message when it wants
922 // us to focus on the first widget in our focus
923 // chain (typically because of a tab).
924 d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::FirstFocusWidget);
925 d->currentFocus->setFocus(Qt::TabFocusReason);
926 break;
927 case XEMBED_FOCUS_LAST:
928 // The container sends this message when it wants
929 // us to focus on the last widget in our focus
930 // chain (typically because of a backtab).
931 d->currentFocus = d->getFocusWidget(QX11EmbedWidgetPrivate::LastFocusWidget);
932 d->currentFocus->setFocus(Qt::BacktabFocusReason);
933 break;
934 default:
935 // Ignore any other XEMBED_FOCUS_IN details.
936 break;
937 }
938 break;
939 case XEMBED_FOCUS_OUT:
940 // The container sends us this message when it wants us
941 // to lose focus and forget about the widget that last
942 // had focus. Typically sent by the container when it
943 // loses focus because of mouse or tab activity. We do
944 // then not want to set focus on anything if we're
945 // activated.
946 if (isActiveWindow())
947 d->clearFocus();
948
949 break;
950 default:
951 // Ignore any other XEMBED messages.
952 break;
953 };
954 } else {
955 // Non-XEMBED client messages are not interesting.
956 }
957
958 break;
959 default:
960 // Ignore all other x11 events.
961 break;
962 }
963
964 // Allow default handling.
965 return QWidget::x11Event(event);
966}
967
968/*!
969 \reimp
970*/
971bool QX11EmbedWidget::event(QEvent *event)
972{
973 if (event->type() == QEvent::ParentChange) {
974 XSelectInput(x11Info().display(), internalWinId(),
975 KeyPressMask | KeyReleaseMask | ButtonPressMask
976 | ButtonReleaseMask
977 | KeymapStateMask | ButtonMotionMask | PointerMotionMask