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

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

trunk: Merged in qt 4.6.2 sources.

File size: 56.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
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 [email protected].
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 <qdatetime.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->setGeometry(-1, -1, 1, 1);
1074
1075 // We need events from the window (activation status) and
1076 // from qApp (keypress/release).
1077 qApp->installEventFilter(this);
1078
1079 // Install X11 event filter.
1080 if (!oldX11EventFilter)
1081 oldX11EventFilter = QCoreApplication::instance()->setEventFilter(x11EventFilter);
1082
1083 XSelectInput(x11Info().display(), internalWinId(),
1084 KeyPressMask | KeyReleaseMask
1085 | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
1086 | KeymapStateMask
1087 | PointerMotionMask
1088 | EnterWindowMask | LeaveWindowMask
1089 | FocusChangeMask
1090 | ExposureMask
1091 | StructureNotifyMask
1092 | SubstructureNotifyMask);
1093
1094 // Make sure our new event mask takes effect as soon as possible.
1095 XFlush(x11Info().display());
1096
1097 // Move input to our focusProxy if this widget is active, and not
1098 // shaded by a modal dialog (in which case isActiveWindow() would
1099 // still return true, but where we must not move input focus).
1100 if (qApp->activeWindow() == window() && !d->isEmbedded())
1101 d->moveInputToProxy();
1102
1103#ifdef QX11EMBED_DEBUG
1104 qDebug() << "QX11EmbedContainer::QX11EmbedContainer: constructed container"
1105 << (void *)this << "with winId" << winId();
1106#endif
1107}
1108
1109/*!
1110 Destructs a QX11EmbedContainer.
1111*/
1112QX11EmbedContainer::~QX11EmbedContainer()
1113{
1114 Q_D(QX11EmbedContainer);
1115 if (d->client) {
1116 XUnmapWindow(x11Info().display(), d->client);
1117 XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(), 0, 0);
1118 }
1119
1120 if (d->xgrab)
1121 XUngrabButton(x11Info().display(), AnyButton, AnyModifier, internalWinId());
1122}
1123
1124
1125QX11EmbedContainer::Error QX11EmbedContainer::error() const {
1126 return d_func()->lastError;
1127}
1128
1129
1130/*! \reimp
1131*/
1132void QX11EmbedContainer::paintEvent(QPaintEvent *)
1133{
1134}
1135
1136/*! \internal
1137
1138 Returns wether or not the windows' embedded flag is set.
1139*/
1140bool QX11EmbedContainerPrivate::isEmbedded() const
1141{
1142 Q_Q(const QX11EmbedContainer);
1143 return ((QHackWidget *)q->window())->topData()->embedded == 1;
1144}
1145
1146/*! \internal
1147
1148 Returns the parentWinId of the window.
1149*/
1150WId QX11EmbedContainerPrivate::topLevelParentWinId() const
1151{
1152 Q_Q(const QX11EmbedContainer);
1153 return ((QHackWidget *)q->window())->topData()->parentWinId;
1154}
1155
1156/*!
1157 If the container has an embedded widget, this function returns
1158 the X11 window ID of the client; otherwise it returns 0.
1159*/
1160WId QX11EmbedContainer::clientWinId() const
1161{
1162 Q_D(const QX11EmbedContainer);
1163 return d->client;
1164}
1165
1166/*!
1167 Instructs the container to embed the X11 window with window ID \a
1168 id. The client widget will then move on top of the container
1169 window and be resized to fit into the container.
1170
1171 The \a id should be the ID of a window controlled by an XEmbed
1172 enabled application, but this is not mandatory. If \a id does not
1173 belong to an XEmbed client widget, then focus handling,
1174 activation, accelerators and other features will not work
1175 properly.
1176*/
1177void QX11EmbedContainer::embedClient(WId id)
1178{
1179 Q_D(QX11EmbedContainer);
1180
1181 if (id == 0) {
1182 d->emitError(InvalidWindowID);
1183 return;
1184 }
1185
1186 // Walk up the tree of parent windows to prevent embedding of ancestors.
1187 WId thisId = internalWinId();
1188 Window rootReturn;
1189 Window parentReturn;
1190 Window *childrenReturn = 0;
1191 unsigned int nchildrenReturn;
1192 do {
1193 if (XQueryTree(x11Info().display(), thisId, &rootReturn,
1194 &parentReturn, &childrenReturn, &nchildrenReturn) == 0) {
1195 d->emitError(InvalidWindowID);
1196 return;
1197 }
1198 if (childrenReturn) {
1199 XFree(childrenReturn);
1200 childrenReturn = 0;
1201 }
1202
1203 thisId = parentReturn;
1204 if (id == thisId) {
1205 d->emitError(InvalidWindowID);
1206 return;
1207 }
1208 } while (thisId != rootReturn);
1209
1210 // watch for property notify events (see below)
1211 XGrabServer(x11Info().display());
1212 XWindowAttributes attrib;
1213 if (!XGetWindowAttributes(x11Info().display(), id, &attrib)) {
1214 XUngrabServer(x11Info().display());
1215 d->emitError(InvalidWindowID);
1216 return;
1217 }
1218 XSelectInput(x11Info().display(), id, attrib.your_event_mask | PropertyChangeMask | StructureNotifyMask);
1219 XUngrabServer(x11Info().display());
1220
1221 // Put the window into WithdrawnState
1222 XUnmapWindow(x11Info().display(), id);
1223 XSync(x11Info().display(), False); // make sure the window is hidden
1224
1225 /*
1226 Wait for notification from the window manager that the window is
1227 in withdrawn state. According to the ICCCM section 4.1.3.1,
1228 we should wait for the WM_STATE property to either be deleted or
1229 set to WithdrawnState.
1230
1231 For safety, we will not wait more than 500 ms, so that we can
1232 preemptively workaround buggy window managers.
1233 */
1234 QTime t;
1235 t.start();
1236
1237 functorData data;
1238 data.id = id;
1239 data.rootWindow = attrib.root;
1240 data.clearedWmState = false;
1241 data.reparentedToRoot = false;
1242
1243 do {
1244 if (t.elapsed() > 500) // time-out after 500 ms
1245 break;
1246
1247 XEvent event;
1248 if (!XCheckIfEvent(x11Info().display(), &event, functor, (XPointer) &data)) {
1249 XSync(x11Info().display(), False);
1250 usleep(50000);
1251 continue;
1252 }
1253
1254 qApp->x11ProcessEvent(&event);
1255 } while (!data.clearedWmState || !data.reparentedToRoot);
1256
1257 // restore the event mask
1258 XSelectInput(x11Info().display(), id, attrib.your_event_mask);
1259
1260 switch (XReparentWindow(x11Info().display(), id, internalWinId(), 0, 0)) {
1261 case BadWindow:
1262 case BadMatch:
1263 d->emitError(InvalidWindowID);
1264 break;
1265 default:
1266 break;
1267 }
1268}
1269
1270/*! \internal
1271
1272 Handles key, activation and focus events for the container.
1273*/
1274bool QX11EmbedContainer::eventFilter(QObject *o, QEvent *event)
1275{
1276 Q_D(QX11EmbedContainer);
1277 switch (event->type()) {
1278 case QEvent::KeyPress:
1279 // Forward any keypresses to our client.
1280 if (o == this && d->client) {
1281 lastKeyEvent.window = d->client;
1282 XSendEvent(x11Info().display(), d->client, false, KeyPressMask, (XEvent *) &lastKeyEvent);
1283 return true;
1284 }
1285 break;
1286 case QEvent::KeyRelease:
1287 // Forward any keyreleases to our client.
1288 if (o == this && d->client) {
1289 lastKeyEvent.window = d->client;
1290 XSendEvent(x11Info().display(), d->client, false, KeyReleaseMask, (XEvent *) &lastKeyEvent);
1291 return true;
1292 }
1293 break;
1294
1295 case QEvent::WindowActivate:
1296 // When our container window is activated, we pass the
1297 // activation message on to our client. Note that X input
1298 // focus is set to our focus proxy. We want to intercept all
1299 // keypresses.
1300 if (o == window() && d->client) {
1301 if (d->clientIsXEmbed) {
1302 sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_ACTIVATE);
1303 } else {
1304 d->checkGrab();
1305 if (hasFocus())
1306 XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time());
1307 }
1308 if (!d->isEmbedded())
1309 d->moveInputToProxy();
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 long *p = (long *)prop_return;
1675 if (nitems_return >= 2)
1676 clientversion = (unsigned int)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 }
1733 } else {
1734 if (!isEmbedded())
1735 moveInputToProxy();
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 // Following Owen Taylor's advice from the XEmbed specification to
1753 // always use CurrentTime when no explicit user action is involved.
1754 XSetInputFocus(q->x11Info().display(), focusProxy->internalWinId(), XRevertToParent, CurrentTime);
1755}
1756
1757/*! \internal
1758
1759 Ask the window manager to give us a default minimum size.
1760*/
1761QSize QX11EmbedContainer::minimumSizeHint() const
1762{
1763 Q_D(const QX11EmbedContainer);
1764 if (!d->client || !d->wmMinimumSizeHint.isValid())
1765 return QWidget::minimumSizeHint();
1766 return d->wmMinimumSizeHint;
1767}
1768
1769/*! \internal
1770
1771*/
1772void QX11EmbedContainerPrivate::checkGrab()
1773{
1774 Q_Q(QX11EmbedContainer);
1775 if (!clientIsXEmbed && q->isActiveWindow() && !q->hasFocus()) {
1776 if (!xgrab) {
1777 XGrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId(),
1778 true, ButtonPressMask, GrabModeSync, GrabModeAsync,
1779 None, None);
1780 }
1781 xgrab = true;
1782 } else {
1783 if (xgrab)
1784 XUngrabButton(q->x11Info().display(), AnyButton, AnyModifier, q->internalWinId());
1785 xgrab = false;
1786 }
1787}
1788
1789/*!
1790 Detaches the client from the embedder. The client will appear as a
1791 standalone window on the desktop.
1792*/
1793void QX11EmbedContainer::discardClient()
1794{
1795 Q_D(QX11EmbedContainer);
1796 if (d->client) {
1797 XResizeWindow(x11Info().display(), d->client, d->clientOriginalRect.width(),
1798 d->clientOriginalRect.height());
1799
1800 d->rejectClient(d->client);
1801 }
1802}
1803
1804QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.