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

Last change on this file since 347 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
978 | FocusChangeMask
979 | ExposureMask | StructureNotifyMask
980 | SubstructureNotifyMask | PropertyChangeMask);
981 }
982 return QWidget::event(event);
983}
984
985/*!
986 \reimp
987*/
988void QX11EmbedWidget::resizeEvent(QResizeEvent *event)
989{
990 if (layout())
991 layout()->update();
992 QWidget::resizeEvent(event);
993}
994
995/*!
996 If the widget is embedded, returns the window ID of the
997 container; otherwize returns 0.
998*/
999WId QX11EmbedWidget::containerWinId() const
1000{
1001 Q_D(const QX11EmbedWidget);
1002 return d->container;
1003}
1004
1005class QX11EmbedContainerPrivate : public QWidgetPrivate
1006{
1007 Q_DECLARE_PUBLIC(QX11EmbedContainer)
1008public:
1009 inline QX11EmbedContainerPrivate()
1010 {
1011 lastError = QX11EmbedContainer::Unknown;
1012 client = 0;
1013 focusProxy = 0;
1014 clientIsXEmbed = false;
1015 xgrab = false;
1016 }
1017
1018 bool isEmbedded() const;
1019 void moveInputToProxy();
1020
1021 void acceptClient(WId window);
1022 void rejectClient(WId window);
1023
1024 void checkGrab();
1025
1026 WId topLevelParentWinId() const;
1027
1028 void emitError(QX11EmbedContainer::Error error) {
1029 Q_Q(QX11EmbedContainer);
1030 lastError = error;
1031 emit q->error(error);
1032 }
1033
1034 WId client;
1035 QWidget *focusProxy;
1036 bool clientIsXEmbed;
1037 bool xgrab;
1038 QRect clientOriginalRect;
1039 QSize wmMinimumSizeHint;
1040
1041 QX11EmbedContainer::Error lastError;
1042
1043 static QX11EmbedContainer *activeContainer;
1044};
1045
1046QX11EmbedContainer *QX11EmbedContainerPrivate::activeContainer = 0;
1047
1048/*!
1049 Creates a QX11EmbedContainer object with the given \a parent.
1050*/
1051QX11EmbedContainer::QX11EmbedContainer(QWidget *parent)
1052 : QWidget(*new QX11EmbedContainerPrivate, parent, 0)
1053{
1054 Q_D(QX11EmbedContainer);
1055 XSetErrorHandler(x11ErrorHandler);
1056
1057 setAttribute(Qt::WA_NativeWindow);
1058 setAttribute(Qt::WA_DontCreateNativeAncestors);
1059 createWinId();
1060
1061 setFocusPolicy(Qt::StrongFocus);
1062 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
1063 // ### PORT setKeyCompression(false);
1064 setAcceptDrops(true);
1065 setEnabled(false);
1066
1067 // Everybody gets a focus proxy, but only one toplevel container's
1068 // focus proxy is actually in use.
1069 d->focusProxy = new QWidget(this);
1070 d->focusProxy->setAttribute(Qt::WA_NativeWindow);
1071 d->focusProxy->setAttribute(Qt::WA_DontCreateNativeAncestors);
1072 d->focusProxy->setGeometry(-1, -1, 1, 1);
1073
1074 // We need events from the window (activation status) and
1075 // from qApp (keypress/release).
1076 qApp->installEventFilter(this);
1077
1078 // Install X11 event filter.
1079 if (!oldX11EventFilter)
1080 oldX11EventFilter = QCoreApplication::instance()->setEventFilter(x11EventFilter);
1081
1082 XSelectInput(x11Info().display(), internalWinId(),
1083 KeyPressMask | KeyReleaseMask
1084 | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
1085 | KeymapStateMask
1086 | PointerMotionMask
1087 | EnterWindowMask | LeaveWindowMask
1088 | FocusChangeMask
1089 | ExposureMask
1090 | StructureNotifyMask
1091 | SubstructureNotifyMask);
1092
1093 // Make sure our new event mask takes effect as soon as possible.
1094 XFlush(x11Info().display());
1095
1096 // Move input to our focusProxy if this widget is active, and not
1097 // shaded by a modal dialog (in which case isActiveWindow() would
1098 // still return true, but where we must not move input focus).
1099 if (qApp->activeWindow() == window() && !d->isEmbedded())
1100 d->moveInputToProxy();
1101
1102#ifdef QX11EMBED_DEBUG
1103 qDebug() << "QX11EmbedContainer::QX11EmbedContainer: constructed container"
1104 << (void *)this << "with winId" << winId();
1105#endif
1106}
1107
1108/*!
1109 Destructs a QX11EmbedContainer.
1110*/
1111QX11EmbedContainer::~QX11EmbedContainer()
1112{
1113 Q_D(QX11EmbedContainer);
1114 if (d->client) {
1115 XUnmapWindow(x11Info().display(), d->client);
1116 XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(), 0, 0);
1117 }
1118
1119 if (d->xgrab)
1120 XUngrabButton(x11Info().display(), AnyButton, AnyModifier, internalWinId());
1121}
1122
1123
1124QX11EmbedContainer::Error QX11EmbedContainer::error() const {
1125 return d_func()->lastError;
1126}
1127
1128
1129/*! \reimp
1130*/
1131void QX11EmbedContainer::paintEvent(QPaintEvent *)
1132{
1133}
1134
1135/*! \internal
1136
1137 Returns wether or not the windows' embedded flag is set.
1138*/
1139bool QX11EmbedContainerPrivate::isEmbedded() const
1140{
1141 Q_Q(const QX11EmbedContainer);
1142 return ((QHackWidget *)q->window())->topData()->embedded == 1;
1143}
1144
1145/*! \internal
1146
1147 Returns the parentWinId of the window.
1148*/
1149WId QX11EmbedContainerPrivate::topLevelParentWinId() const
1150{
1151 Q_Q(const QX11EmbedContainer);
1152 return ((QHackWidget *)q->window())->topData()->parentWinId;
1153}
1154
1155/*!
1156 If the container has an embedded widget, this function returns
1157 the X11 window ID of the client; otherwise it returns 0.
1158*/
1159WId QX11EmbedContainer::clientWinId() const
1160{
1161 Q_D(const QX11EmbedContainer);
1162 return d->client;
1163}
1164
1165/*!
1166 Instructs the container to embed the X11 window with window ID \a
1167 id. The client widget will then move on top of the container
1168 window and be resized to fit into the container.
1169
1170 The \a id should be the ID of a window controlled by an XEmbed
1171 enabled application, but this is not mandatory. If \a id does not
1172 belong to an XEmbed client widget, then focus handling,
1173 activation, accelerators and other features will not work
1174 properly.
1175*/
1176void QX11EmbedContainer::embedClient(WId id)
1177{
1178 Q_D(QX11EmbedContainer);
1179
1180 if (id == 0) {
1181 d->emitError(InvalidWindowID);
1182 return;
1183 }
1184
1185 // Walk up the tree of parent windows to prevent embedding of ancestors.
1186 WId thisId = internalWinId();
1187 Window rootReturn;
1188 Window parentReturn;
1189 Window *childrenReturn = 0;
1190 unsigned int nchildrenReturn;
1191 do {
1192 if (XQueryTree(x11Info().display(), thisId, &rootReturn,
1193 &parentReturn, &childrenReturn, &nchildrenReturn) == 0) {
1194 d->emitError(InvalidWindowID);
1195 return;
1196 }
1197 if (childrenReturn) {
1198 XFree(childrenReturn);
1199 childrenReturn = 0;
1200 }
1201
1202 thisId = parentReturn;
1203 if (id == thisId) {
1204 d->emitError(InvalidWindowID);
1205 return;
1206 }
1207 } while (thisId != rootReturn);
1208
1209 // watch for property notify events (see below)
1210 XGrabServer(x11Info().display());
1211 XWindowAttributes attrib;
1212 if (!XGetWindowAttributes(x11Info().display(), id, &attrib)) {
1213 XUngrabServer(x11Info().display());
1214 d->emitError(InvalidWindowID);
1215 return;
1216 }
1217 XSelectInput(x11Info().display(), id, attrib.your_event_mask | PropertyChangeMask | StructureNotifyMask);
1218 XUngrabServer(x11Info().display());
1219
1220 // Put the window into WithdrawnState
1221 XUnmapWindow(x11Info().display(), id);
1222 XSync(x11Info().display(), False); // make sure the window is hidden
1223
1224 /*
1225 Wait for notification from the window manager that the window is
1226 in withdrawn state. According to the ICCCM section 4.1.3.1,
1227 we should wait for the WM_STATE property to either be deleted or
1228 set to WithdrawnState.
1229
1230 For safety, we will not wait more than 500 ms, so that we can
1231 preemptively workaround buggy window managers.
1232 */
1233 QTime t;
1234 t.start();
1235
1236 functorData data;
1237 data.id = id;
1238 data.rootWindow = attrib.root;
1239 data.clearedWmState = false;
1240 data.reparentedToRoot = false;
1241
1242 do {
1243 if (t.elapsed() > 500) // time-out after 500 ms
1244 break;
1245
1246 XEvent event;
1247 if (!XCheckIfEvent(x11Info().display(), &event, functor, (XPointer) &data)) {
1248 XSync(x11Info().display(), False);
1249 usleep(50000);
1250 continue;
1251 }
1252
1253 qApp->x11ProcessEvent(&event);
1254 } while (!data.clearedWmState || !data.reparentedToRoot);
1255
1256 // restore the event mask
1257 XSelectInput(x11Info().display(), id, attrib.your_event_mask);
1258
1259 switch (XReparentWindow(x11Info().display(), id, internalWinId(), 0, 0)) {
1260 case BadWindow:
1261 case BadMatch:
1262 d->emitError(InvalidWindowID);
1263 break;
1264 default:
1265 break;
1266 }
1267}
1268
1269/*! \internal
1270
1271 Handles key, activation and focus events for the container.
1272*/
1273bool QX11EmbedContainer::eventFilter(QObject *o, QEvent *event)
1274{
1275 Q_D(QX11EmbedContainer);
1276 switch (event->type()) {
1277 case QEvent::KeyPress:
1278 // Forward any keypresses to our client.
1279 if (o == this && d->client) {
1280 lastKeyEvent.window = d->client;
1281 XSendEvent(x11Info().display(), d->client, false, KeyPressMask, (XEvent *) &lastKeyEvent);
1282 return true;
1283 }
1284 break;
1285 case QEvent::KeyRelease:
1286 // Forward any keyreleases to our client.
1287 if (o == this && d->client) {
1288 lastKeyEvent.window = d->client;
1289 XSendEvent(x11Info().display(), d->client, false, KeyReleaseMask, (XEvent *) &lastKeyEvent);
1290 return true;
1291 }
1292 break;
1293
1294 case QEvent::WindowActivate:
1295 // When our container window is activated, we pass the
1296 // activation message on to our client. Note that X input
1297 // focus is set to our focus proxy. We want to intercept all
1298 // keypresses.
1299 if (o == window() && d->client) {
1300 if (!d->isEmbedded() && d->activeContainer == this)
1301 d->moveInputToProxy();
1302
1303 if (d->clientIsXEmbed) {
1304 sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_ACTIVATE);
1305 } else {
1306 d->checkGrab();
1307 if (hasFocus())
1308 XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time());
1309 }
1310 }
1311 break;
1312 case QEvent::WindowDeactivate:
1313 // When our container window is deactivated, we pass the
1314 // deactivation message to our client.
1315 if (o == window() && d->client) {
1316 if (d->clientIsXEmbed)
1317 sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_DEACTIVATE);
1318 else
1319 d->checkGrab();
1320 }
1321 break;
1322 case QEvent::FocusIn:
1323 // When receiving FocusIn events generated by Tab or Backtab,
1324 // we pass focus on to our client. Any mouse activity is sent
1325 // directly to the client, and it will ask us for focus with
1326 // XEMBED_REQUEST_FOCUS.
1327 if (o == this && d->client) {
1328 if (!d->isEmbedded())
1329 d->activeContainer = this;
1330
1331 if (d->clientIsXEmbed) {
1332 if (!d->isEmbedded())
1333 d->moveInputToProxy();
1334
1335 QFocusEvent *fe = (QFocusEvent *)event;
1336 switch (fe->reason()) {
1337 case Qt::TabFocusReason:
1338 sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST);
1339 break;
1340 case Qt::BacktabFocusReason:
1341 sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_LAST);
1342 break;
1343 default:
1344 sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
1345 break;
1346 }
1347 } else {
1348 d->checkGrab();
1349 XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time());
1350 }
1351 }
1352
1353 break;
1354 case QEvent::FocusOut: {
1355 // When receiving a FocusOut, we ask our client to remove its
1356 // focus.
1357 if (o == this && d->client) {
1358 if (!d->isEmbedded()) {
1359 d->activeContainer = 0;
1360 if (isActiveWindow())
1361 d->moveInputToProxy();
1362 }
1363
1364 if (d->clientIsXEmbed) {
1365 QFocusEvent *fe = (QFocusEvent *)event;
1366 if (o == this && d->client && fe->reason() != Qt::ActiveWindowFocusReason)
1367 sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_OUT);
1368 } else {
1369 d->checkGrab();
1370 }
1371 }
1372 }
1373 break;
1374
1375 case QEvent::Close: {
1376 if (o == this && d->client) {
1377 // Unmap the client and reparent it to the root window.
1378 // Wait until the messages have been processed. Then ask
1379 // the window manager to delete the window.
1380 XUnmapWindow(x11Info().display(), d->client);
1381 XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(), 0, 0);
1382 XSync(x11Info().display(), false);
1383
1384 XEvent ev;
1385 memset(&ev, 0, sizeof(ev));
1386 ev.xclient.type = ClientMessage;
1387 ev.xclient.window = d->client;
1388 ev.xclient.message_type = ATOM(WM_PROTOCOLS);
1389 ev.xclient.format = 32;
1390 ev.xclient.data.s[0] = ATOM(WM_DELETE_WINDOW);
1391 XSendEvent(x11Info().display(), d->client, false, NoEventMask, &ev);
1392
1393 XFlush(x11Info().display());
1394 d->client = 0;
1395 d->clientIsXEmbed = false;
1396 d->wmMinimumSizeHint = QSize();
1397 updateGeometry();
1398 setEnabled(false);
1399 update();
1400
1401 emit clientClosed();
1402 }
1403 }
1404 default:
1405 break;
1406 }
1407
1408 return QWidget::eventFilter(o, event);
1409}
1410
1411/*! \internal
1412
1413 Handles X11 events for the container.
1414*/
1415bool QX11EmbedContainer::x11Event(XEvent *event)
1416{
1417 Q_D(QX11EmbedContainer);
1418
1419 switch (event->type) {
1420 case CreateNotify:
1421 // The client created an embedded window.
1422 if (d->client)
1423 d->rejectClient(event->xcreatewindow.window);
1424 else
1425 d->acceptClient(event->xcreatewindow.window);
1426 break;
1427 case DestroyNotify:
1428 if (event->xdestroywindow.window == d->client) {
1429 // The client died.
1430 d->client = 0;
1431 d->clientIsXEmbed = false;
1432 d->wmMinimumSizeHint = QSize();
1433 updateGeometry();
1434 update();
1435 setEnabled(false);
1436 emit clientClosed();
1437 }
1438 break;
1439 case ReparentNotify:
1440 // The client sends us this if it reparents itself out of our
1441 // widget.
1442 if (event->xreparent.window == d->client && event->xreparent.parent != internalWinId()) {
1443 d->client = 0;
1444 d->clientIsXEmbed = false;
1445 d->wmMinimumSizeHint = QSize();
1446 updateGeometry();
1447 update();
1448 setEnabled(false);
1449 emit clientClosed();
1450 } else if (event->xreparent.parent == internalWinId()) {
1451 // The client reparented itself into this window.
1452 if (d->client)
1453 d->rejectClient(event->xreparent.window);
1454 else
1455 d->acceptClient(event->xreparent.window);
1456 }
1457 break;
1458 case ClientMessage: {
1459 if (event->xclient.message_type == ATOM(_XEMBED)) {
1460 // Ignore XEMBED messages not to ourselves
1461 if (event->xclient.window != internalWinId())
1462 break;
1463
1464 // Receiving an XEmbed message means the client
1465 // is an XEmbed client.
1466 d->clientIsXEmbed = true;
1467
1468 Time msgtime = (Time) event->xclient.data.l[0];
1469 if (msgtime > X11->time)
1470 X11->time = msgtime;
1471
1472 switch (event->xclient.data.l[1]) {
1473 case XEMBED_REQUEST_FOCUS: {
1474 // This typically happens when the client gets focus
1475 // because of a mouse click.
1476 if (!hasFocus())
1477 setFocus(Qt::OtherFocusReason);
1478
1479 // The message is passed along to the topmost container
1480 // that eventually responds with a XEMBED_FOCUS_IN
1481 // message. The focus in message is passed all the way
1482 // back until it reaches the original focus
1483 // requestor. In the end, not only the original client
1484 // has focus, but also all its ancestor containers.
1485 if (d->isEmbedded()) {
1486 // If our window's embedded flag is set, then
1487 // that suggests that we are part of a client. The
1488 // parentWinId will then point to an container to whom
1489 // we must pass this message.
1490 sendXEmbedMessage(d->topLevelParentWinId(), x11Info().display(), XEMBED_REQUEST_FOCUS);
1491 } else {
1492 // Our window's embedded flag is not set,
1493 // so we are the topmost container. We respond to
1494 // the focus request message with a focus in
1495 // message. This message will pass on from client
1496 // to container to client until it reaches the
1497 // originator of the XEMBED_REQUEST_FOCUS message.
1498 sendXEmbedMessage(d->client, x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
1499 }
1500
1501 break;
1502 }
1503 case XEMBED_FOCUS_NEXT:
1504 // Client sends this event when it received a tab
1505 // forward and was at the end of its focus chain. If
1506 // we are the only widget in the focus chain, we send
1507 // ourselves a FocusIn event.
1508 if (d->focus_next != this) {
1509 focusNextPrevChild(true);
1510 } else {
1511 QFocusEvent event(QEvent::FocusIn, Qt::TabFocusReason);
1512 qApp->sendEvent(this, &event);
1513 }
1514
1515 break;
1516 case XEMBED_FOCUS_PREV:
1517 // Client sends this event when it received a backtab
1518 // and was at the start of its focus chain. If we are
1519 // the only widget in the focus chain, we send
1520 // ourselves a FocusIn event.
1521 if (d->focus_next != this) {
1522 focusNextPrevChild(false);
1523 } else {
1524 QFocusEvent event(QEvent::FocusIn, Qt::BacktabFocusReason);
1525 qApp->sendEvent(this, &event);
1526 }
1527
1528 break;
1529 default:
1530 break;
1531 }
1532 }
1533 }
1534 break;
1535 case XButtonPress:
1536 if (!d->clientIsXEmbed) {
1537 setFocus(Qt::MouseFocusReason);
1538 XAllowEvents(x11Info().display(), ReplayPointer, CurrentTime);
1539 return true;
1540 }
1541 break;
1542 case XButtonRelease:
1543 if (!d->clientIsXEmbed)
1544 XAllowEvents(x11Info().display(), SyncPointer, CurrentTime);
1545 break;
1546 default:
1547 break;
1548 }
1549
1550 return QWidget::x11Event(event);
1551}
1552
1553/*! \internal
1554
1555 Whenever the container is resized, we need to resize our client.
1556*/
1557void QX11EmbedContainer::resizeEvent(QResizeEvent *)
1558{
1559 Q_D(QX11EmbedContainer);
1560 if (d->client)
1561 XResizeWindow(x11Info().display(), d->client, width(), height());
1562}
1563
1564/*! \internal
1565
1566 We use the QShowEvent to signal to our client that we want it to
1567 map itself. We do this by changing its window property
1568 XEMBED_INFO. The client will get an X11 PropertyNotify.
1569*/
1570void QX11EmbedContainer::showEvent(QShowEvent *)
1571{
1572 Q_D(QX11EmbedContainer);
1573 if (d->client) {
1574 long data[] = {XEMBED_VERSION, XEMBED_MAPPED};
1575 XChangeProperty(x11Info().display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32,
1576 PropModeReplace, (unsigned char *) data, 2);
1577 }
1578}
1579
1580/*! \internal
1581
1582 We use the QHideEvent to signal to our client that we want it to
1583 unmap itself. We do this by changing its window property
1584 XEMBED_INFO. The client will get an X11 PropertyNotify.
1585*/
1586void QX11EmbedContainer::hideEvent(QHideEvent *)
1587{
1588 Q_D(QX11EmbedContainer);
1589 if (d->client) {
1590 long data[] = {XEMBED_VERSION, XEMBED_MAPPED};
1591 XChangeProperty(x11Info().display(), d->client, ATOM(_XEMBED_INFO), ATOM(_XEMBED_INFO), 32,
1592 PropModeReplace, (unsigned char *) data, 2);
1593 }
1594}
1595
1596/*!
1597 \reimp
1598*/
1599bool QX11EmbedContainer::event(QEvent *event)
1600{
1601 if (event->type() == QEvent::ParentChange) {
1602 XSelectInput(x11Info().display(), internalWinId(),
1603 KeyPressMask | KeyReleaseMask
1604 | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
1605 | KeymapStateMask
1606 | PointerMotionMask
1607 | EnterWindowMask | LeaveWindowMask
1608 | FocusChangeMask
1609 | ExposureMask
1610 | StructureNotifyMask
1611 | SubstructureNotifyMask);
1612 }
1613 return QWidget::event(event);
1614}
1615
1616/*! \internal
1617
1618 Rejects a client window by reparenting it to the root window. The
1619 client will receive a reparentnotify, and will most likely assume
1620 that the container has shut down. The XEmbed protocol does not
1621 define any way to reject a client window, but this is a clean way
1622 to do it.
1623*/
1624void QX11EmbedContainerPrivate::rejectClient(WId window)
1625{
1626 Q_Q(QX11EmbedContainer);
1627 q->setEnabled(false);
1628 XRemoveFromSaveSet(q->x11Info().display(), client);
1629 XReparentWindow(q->x11Info().display(), window, q->x11Info().appRootWindow(), 0, 0);
1630}
1631
1632/*! \internal
1633
1634 Accepts a client by mapping it, resizing it and optionally
1635 activating and giving it logical focusing through XEMBED messages.
1636*/
1637void QX11EmbedContainerPrivate::acceptClient(WId window)
1638{
1639 Q_Q(QX11EmbedContainer);
1640 client = window;
1641 q->setEnabled(true);
1642
1643 // This tells Qt that we wish to forward DnD messages to
1644 // our client.
1645 if (!extra)
1646 createExtra();
1647 extraData()->xDndProxy = client;
1648
1649 unsigned int version = XEmbedVersion();
1650
1651 Atom actual_type_return;
1652 int actual_format_return;
1653 unsigned long nitems_return = 0;
1654 unsigned long bytes_after_return;
1655 unsigned char *prop_return = 0;
1656 unsigned int clientversion = 0;
1657
1658 // Add this client to our saveset, so if we crash, the client window
1659 // doesn't get destroyed. This is useful for containers that restart
1660 // automatically after a crash, because it can simply reembed its clients
1661 // without having to restart them (KDE panel).
1662 XAddToSaveSet(q->x11Info().display(), client);
1663
1664 // XEmbed clients have an _XEMBED_INFO property in which we can
1665 // fetch the version
1666 if (XGetWindowProperty(q->x11Info().display(), client, ATOM(_XEMBED_INFO), 0, 2, false,
1667 ATOM(_XEMBED_INFO), &actual_type_return, &actual_format_return,
1668 &nitems_return, &bytes_after_return, &prop_return) == Success) {
1669
1670 if (actual_type_return != None && actual_format_return != 0) {
1671 // Clients with the _XEMBED_INFO property are XEMBED clients.
1672 clientIsXEmbed = true;
1673
1674 unsigned int *p = (unsigned int *)prop_return;
1675 if (nitems_return >= 2)
1676 clientversion = p[0];
1677 }
1678
1679 XFree(prop_return);
1680 }
1681
1682 // Store client window's original size and placement.
1683 Window root;
1684 int x_return, y_return;
1685 unsigned int width_return, height_return, border_width_return, depth_return;
1686 XGetGeometry(q->x11Info().display(), client, &root, &x_return, &y_return,
1687 &width_return, &height_return, &border_width_return, &depth_return);
1688 clientOriginalRect.setCoords(x_return, y_return,
1689 x_return + width_return - 1,
1690 y_return + height_return - 1);
1691
1692 // Ask the client for its minimum size.
1693 XSizeHints size;
1694 long msize;
1695 if (XGetWMNormalHints(q->x11Info().display(), client, &size, &msize) && (size.flags & PMinSize)) {
1696 wmMinimumSizeHint = QSize(size.min_width, size.min_height);
1697 q->updateGeometry();
1698 }
1699
1700 // The container should set the data2 field to the lowest of its
1701 // supported version number and that of the client (from
1702 // _XEMBED_INFO property).
1703 unsigned int minversion = version > clientversion ? clientversion : version;
1704 sendXEmbedMessage(client, q->x11Info().display(), XEMBED_EMBEDDED_NOTIFY, q->internalWinId(), minversion);
1705 XMapWindow(q->x11Info().display(), client);
1706
1707 // Resize it, but no smaller than its minimum size hint.
1708 XResizeWindow(q->x11Info().display(),
1709 client,
1710 qMax(q->width(), wmMinimumSizeHint.width()),
1711 qMax(q->height(), wmMinimumSizeHint.height()));
1712 q->update();
1713
1714 // Not mentioned in the protocol is that if the container
1715 // is already active, the client must be activated to work
1716 // properly.
1717 if (q->window()->isActiveWindow())
1718 sendXEmbedMessage(client, q->x11Info().display(), XEMBED_WINDOW_ACTIVATE);
1719
1720 // Also, if the container already has focus, then it must
1721 // send a focus in message to its new client; otherwise we ask
1722 // it to remove focus.
1723 if (q->focusWidget() == q && q->hasFocus())
1724 sendXEmbedMessage(client, q->x11Info().display(), XEMBED_FOCUS_IN, XEMBED_FOCUS_FIRST);
1725 else
1726 sendXEmbedMessage(client, q->x11Info().display(), XEMBED_FOCUS_OUT);
1727
1728 if (!clientIsXEmbed) {
1729 checkGrab();
1730 if (q->hasFocus()) {
1731 XSetInputFocus(q->x11Info().display(), client, XRevertToParent, x11Time());
1732 } else {
1733 if (!isEmbedded())
1734 moveInputToProxy();
1735 }
1736 }
1737
1738 emit q->clientIsEmbedded();
1739}
1740
1741/*! \internal
1742
1743 Moves X11 keyboard input focus to the focusProxy, unless the focus
1744 is there already. When X11 keyboard input focus is on the
1745 focusProxy, which is a child of the container and a sibling of the
1746 client, X11 keypresses and keyreleases will always go to the proxy
1747 and not to the client.
1748*/
1749void QX11EmbedContainerPrivate::moveInputToProxy()
1750{
1751 Q_Q(QX11EmbedContainer);
1752 WId focus;
1753 int revert_to;
1754 XGetInputFocus(q->x11Info().display(), &focus, &revert_to);
1755 if (focus != focusProxy->internalWinId())
1756 XSetInputFocus(q->x11Info().display(), focusProxy->internalWinId(), XRevertToParent, x11Time());
1757}
1758
1759/*! \internal
1760
1761 Ask the window manager to give us a default minimum size.
1762*/
1763QSize QX11EmbedContainer::minimumSizeHint() const
1764{
1765 Q_D(const QX11EmbedContainer);
1766 if (!d->client || !d->wmMinimumSizeHint.isValid())
1767 return QWidget::minimumSizeHint();
1768 return d->wmMinimumSizeHint;
1769}
1770
1771/*! \internal
1772
1773*/
1774void QX11EmbedContainerPrivate::checkGrab()
1775{
1776 Q_Q(QX11EmbedContainer);
1777 if (!clientIsXEmbed && q->isActiveWindow() && !q->hasFocus()) {
1778 if (!xgrab) {
1779 XGrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId(),
1780 true, ButtonPressMask, GrabModeSync, GrabModeAsync,
1781 None, None);
1782 }
1783 xgrab = true;
1784 } else {
1785 if (xgrab)
1786 XUngrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId());
1787 xgrab = false;
1788 }
1789}
1790
1791/*!
1792 Detaches the client from the embedder. The client will appear as a
1793 standalone window on the desktop.
1794*/
1795void QX11EmbedContainer::discardClient()
1796{
1797 Q_D(QX11EmbedContainer);
1798 if (d->client) {
1799 XResizeWindow(x11Info().display(), d->client, d->clientOriginalRect.width(),
1800 d->clientOriginalRect.height());
1801
1802 d->rejectClient(d->client);
1803 }
1804}
1805
1806QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.