/**************************************************************************** Implementation of QXEmbed class Copyright (C) 1999-2002 Trolltech AS This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *****************************************************************************/ // L-000: About comments marked with Lxxxx. // // These comments represent an attempt to provide a more adequate // documentation to KDE developpers willing to modify QXEmbed. Keep in // mind that these comments were written long after most of the code. // Please improve them if you spot something wrong or missing // (Leon Bottou, 26-10-2003). // // Relevant documents: // - QXEmbed developper documentation // (see comments in qxembed.h) // - Xlib Reference Manual // (sections about focus, reparenting, window management) // - ICCCM Manual // (window management) // - XEMBED specification // (http://www.freedesktop.org/Standards/xembed-spec) // - XPLAIN and XEMBED. // // - Accumulated community knowledge. // // // // #include #include #include #include #include #include // L0001: QXEmbed works only under X windows. #ifdef Q_WS_X11 # include # include # include # include # define XK_MISCELLANY # define XK_LATIN1 # include # include # include // L0002: Is file config.h KDE specific? # include # ifdef HAVE_UNISTD_H # include # ifdef HAVE_USLEEP # define USLEEP(x) usleep(x) # else # define USLEEP(x) sleep(0) # endif # else # define USLEEP(x) sleep(0) # endif # include "qxembed.h" // L0003: This keysym is used for focus navigation. # ifndef XK_ISO_Left_Tab # define XK_ISO_Left_Tab 0xFE20 # endif // L0004: Conflicts between X11 and Qt definitions. const int XFocusOut = FocusOut; const int XFocusIn = FocusIn; const int XKeyPress = KeyPress; const int XKeyRelease = KeyRelease; # undef KeyRelease # undef KeyPress # undef FocusOut # undef FocusIn // L0005: Variables defined in qapplication_x11.cpp extern Atom qt_wm_protocols; extern Atom qt_wm_delete_window; extern Atom qt_wm_take_focus; extern Atom qt_wm_state; // L0006: X11 atoms private to QXEmbed static Atom xembed = 0; static Atom context_help = 0; // L0007: Xembed message codes (see XEmbed spec) #define XEMBED_EMBEDDED_NOTIFY 0 #define XEMBED_WINDOW_ACTIVATE 1 #define XEMBED_WINDOW_DEACTIVATE 2 #define XEMBED_REQUEST_FOCUS 3 #define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_OUT 5 #define XEMBED_FOCUS_NEXT 6 #define XEMBED_FOCUS_PREV 7 // L0008: Xembed message details (see XEmbed spec) // -- XEMBED_FOCUS_IN: #define XEMBED_FOCUS_CURRENT 0 #define XEMBED_FOCUS_FIRST 1 #define XEMBED_FOCUS_LAST 2 // L0100: Private data held by the QXEmbed object. // This belongs to the embedder side. class QXEmbedData { public: QXEmbedData(){ autoDelete = true; xplain = false; xgrab = false; mapAfterRelease = false; lastPos = TQPoint(0,0); } ~QXEmbedData(){} bool autoDelete; // L0101: See L2600 bool xplain; // L0102: See L1100 bool xgrab; // L0103: See L2800 bool mapAfterRelease; TQWidget* focusProxy; // L0104: See XEmbed spec TQPoint lastPos; // L0105: See L1390 }; namespace { // L0200: This application wide event filter handles focus // issues in the embedded client. class QXEmbedAppFilter : public TQObject { public: QXEmbedAppFilter() { tqApp->installEventFilter( this ); } ~QXEmbedAppFilter() { } bool eventFilter( TQObject *, TQEvent * ); }; } // L0201: See L0200, L0740 static QXEmbedAppFilter* filter = 0; // L0202: See L0610, L0730 static TQPtrDict > *focusMap = 0; // L0203: See L0660, L1400, L1450 static XKeyEvent last_key_event; // L0300: This class gives access protected members of class TQWidget. // Function focusData() is useful to reimplement tab focus management // (L0620) Function topData() returns a structure QTLWExtra containing // information unique to toplevel windows. This structure contains two // members for the sole use of QXEmbed. Flag `embedded' indicates whether // the toplevel window is embedded using the XEMBED protocol (L0680). // Handle `parentWinId' then records the id of the embedding window. class QPublicWidget : public TQWidget { public: TQTLWExtra* topData() { return TQWidget::topData(); } TQFocusData *focusData(){ return TQWidget::focusData(); } bool focusNextPrev(bool b) { return focusNextPrevChild(b); } }; // L0400: This sets a very low level filter for X11 messages. // See qapplication_x11.cpp typedef int (*QX11EventFilter) (XEvent*); extern QX11EventFilter qt_set_x11_event_filter (QX11EventFilter filter); static QX11EventFilter oldFilter = 0; // L0500: Helper to send XEmbed messages. static void sendXEmbedMessage( WId window, long message, long detail = 0, long data1 = 0, long data2 = 0) { if (!window) return; XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = window; ev.xclient.message_type = xembed; ev.xclient.format = 32; ev.xclient.data.l[0] = GET_QT_X_TIME(); ev.xclient.data.l[1] = message; ev.xclient.data.l[2] = detail; ev.xclient.data.l[3] = data1; ev.xclient.data.l[4] = data2; XSendEvent(qt_xdisplay(), window, false, NoEventMask, &ev); } // L0501: Helper to send ICCCM Client messages. // See X11 ICCCM Specification. static void sendClientMessage(Window window, Atom a, long x) { if (!window) return; XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = window; ev.xclient.message_type = a; ev.xclient.format = 32; ev.xclient.data.l[0] = x; ev.xclient.data.l[1] = GET_QT_X_TIME(); XSendEvent(qt_xdisplay(), window, false, NoEventMask, &ev); } // L0502: Helper to send fake X11 focus messages. // See X11 Reference Manual and Window Management stuff. static void sendFocusMessage(Window window, int type, int mode, int detail) { if (!window) return; XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xfocus.type = type; ev.xfocus.window = window; ev.xfocus.mode = mode; ev.xfocus.detail = detail; XSendEvent(qt_xdisplay(), window, false, FocusChangeMask, &ev); } // ------------------------------------------------------------ // L0600: MOST OF WHAT FOLLOWS CONCERNS THE CLIENT SIDE. // The following code mostly executes inside a Qt application swallowed // by QXEmbed widget. It mostly consists of event filters that fight // the normal Qt mechanisms in order to implement the XEMBED protocol. // All this would be a lot simpler if it was implemented by Qt itself. // L0610: This event filter receives all Qt events. Its main purpose is to // capture the Qt focus events in the embedded client in order to // implement the XEMBED protocol. // // Let's start with a few reminders: // // - X11 only has the concept of the "X11 focus window". This window // basically receives all key events. The ICCCM conventions define // how the window manager and the applications must cooperate to // choose the X11 focus window. // // - Most toolkits, including Qt, maintain the concepts of 'active // widget' and 'Qt focus widget'. A toplevel widget is active when // the X11 focus is set to one of its children. By extension a // widget is active when its toplevel widget is active. There is one // Qt focus widget for each toplevel widget. When the toplevel // widget is active, all key events are sent to the Qt focus widget, // regardless of which descendant of the toplevel window has the X11 // focus. Widgets can adjust their appearance according to both // their activation and focus states. The Qt FocusIn and FocusOut // events indicate when a widget simultaneously is active and has // the Qt focus. // // The XEMBED protocol defines ways to communicate abouth both // activation and focus. The embedded client is active as soon as the // embedding window is active (L0676, L0677). A widget in the embedded // client receives key events when (1) it has the Qt focus in the // embedded application, and (2) the QXEmbed widget in the embedding // application is active and has the Qt focus. The Qt library in the // embedded application is unaware of the focus status of the QXEmbed // widget. We must make sure it does the right thing regarding the // sending of focus events and the visual appearance of the focussed // widgets. When the QXEmbed widget looses the Qt focus, we clear the // focus in the embedded client (L1570, L0688). Conversely, when // the QXEmbed widget gains the Qt focus, we restore the Qt focus // window in the embedded client (L1530, L0680, L0683). // Variable focusMap is used to remember which was the Qt focus // widget in the embedded application. All this would be a lot // simpler if it was implemented inside Qt... // // The XPLAIN protocol is much less refined in this respect. // The activation status of the embedded client simply reflect // the focus status of the QXEmbed widget. This is achieved // by sending fake X11 focus message to the client (L1521, L1561). // A passive button grab (L2800) intercepts mouse activity in the // embedded client and sets the Qt focus to the QXEmbed widget // when this happens (L2060). This can be achieved without // cooperation from the client. bool QXEmbedAppFilter::eventFilter( TQObject *o, TQEvent * e) { static bool obeyFocus = false; switch ( e->type() ) { case TQEvent::MouseButtonPress: // L0612: This will become clear with L0614 if ( !((TQWidget*)o)->isActiveWindow() ) obeyFocus = true; break; case TQEvent::FocusIn: // L0613: FocusIn events either occur because the widget already was // active and has just been given the Qt focus (L0614) or // because the widget already had the Qt focus and just became // active (L0615). if ( TQT_BASE_OBJECT(tqApp->tqfocusWidget()) == TQT_BASE_OBJECT(o) && ((QPublicWidget*)tqApp->tqfocusWidget()->tqtopLevelWidget())->topData()->embedded ) { TQFocusEvent* fe = (TQFocusEvent*) e; if ( obeyFocus || fe->reason() != TQFocusEvent::ActiveWindow /*|| fe->reason() == TQFocusEvent::Mouse || fe->reason() == TQFocusEvent::Shortcut*/ ) { // L0614: A widget in the embedded client was just given the Qt focus. // Variable `obeyFocus' suggests that this is the result of mouse // activity in the client. The XEMBED_REQUEST_FOCUS message causes // the embedding widget to take the Qt focus (L2085). #ifdef USE_QT4 WId window = ((QPublicWidget*)tqApp->tqfocusWidget()->tqtopLevelWidget())->effectiveWinId(); #else // USE_QT4 WId window = ((QPublicWidget*)tqApp->tqfocusWidget()->tqtopLevelWidget())->topData()->parentWinId; #endif // USE_QT4 focusMap->remove( tqApp->tqfocusWidget()->tqtopLevelWidget() ); sendXEmbedMessage( window, XEMBED_REQUEST_FOCUS ); } else if ( fe->reason() == TQFocusEvent::ActiveWindow ) { // L0615: Both the embedder and the embedded client became active. // But we do not know whether the QXEmbed widget has the Qt focus. // So we clear the Qt focus for now. If indeed the QXEmbed widget // has the focus, it will receive a FocusIn message (L1530) and // tell us to restore the focus (L0680, L0683). focusMap->remove( tqApp->tqfocusWidget()->tqtopLevelWidget() ); focusMap->insert( tqApp->tqfocusWidget()->tqtopLevelWidget(), new TQGuardedPtr(tqApp->tqfocusWidget()->tqtopLevelWidget()->tqfocusWidget() ) ); // L0616: tqApp->tqfocusWidget() might belong to a modal dialog and not be // equal to tqApp->tqfocusWidget()->tqtopLevelWidget()->tqfocusWidget() ! tqApp->tqfocusWidget()->clearFocus(); // L0617: ??? [why not {obeyFocus=false; return true;} here?] } obeyFocus = false; } break; case TQEvent::KeyPress: if (TQT_BASE_OBJECT(tqApp->tqfocusWidget()) == TQT_BASE_OBJECT(o) && ((QPublicWidget*)tqApp->tqfocusWidget()->tqtopLevelWidget())->topData()->embedded ) { // L0620: The following code replaces the Qt code that // handles focus focus changes with the tab key. See the // XEMBED specification for details. The keypress event // arrives here after an interesting itinerary. It is first // saved in the embedding application (L0660). After being // rejected for tab navigation in the embedding application // (L1901), it gets forwarded to the embedded client // (L1400) and arrives here. Depending on the status of // the tab chain in the embedded client, focus navigation // messages are sent back to the embedding application // (L0653, L0654) which then performs tab navigation // (L2081). TQKeyEvent *k = (TQKeyEvent *)e; TQWidget *w = tqApp->tqfocusWidget(); // L0621: The following tests are copied from TQWidget::event(). bool res = false; bool tabForward = true; if ( !(k->state() & ControlButton || k->state() & AltButton) ) { if ( k->key() == Key_Backtab || (k->key() == Key_Tab && (k->state() & ShiftButton)) ) { #ifdef USE_QT4 res = ((QPublicWidget*)w)->focusNextPrev( tabForward = false ); #else // USE_QT4 TQFocusEvent::setReason( TQFocusEvent::Backtab ); res = ((QPublicWidget*)w)->focusNextPrev( tabForward = false ); TQFocusEvent::resetReason(); #endif // USE_QT4 } else if ( k->key() == Key_Tab ) { #ifdef USE_QT4 res = ((QPublicWidget*)w)->focusNextPrev( tabForward = true ); #else // USE_QT4 TQFocusEvent::setReason( TQFocusEvent::Tab ); res = ((QPublicWidget*)w)->focusNextPrev( tabForward = true ); TQFocusEvent::resetReason(); #endif // USE_QT4 } } if (res) { // L0625: We changed the focus because of tab/backtab key // Now check whether we have been looping around. TQFocusData *fd = ((QPublicWidget*)w)->focusData(); #ifdef USE_QT4 WId window = ((QPublicWidget*)w->tqtopLevelWidget())->effectiveWinId(); #else // USE_QT4 WId window = ((QPublicWidget*)w->tqtopLevelWidget())->topData()->parentWinId; #endif // USE_QT4 TQWidget *cw = 0; TQWidget *fw = fd->home(); if (tabForward && window) { while (cw != w && cw != fw && cw != w->tqtopLevelWidget()) cw = fd->prev(); if (cw != w) sendXEmbedMessage( window, XEMBED_FOCUS_NEXT ); } else if (window) { while (cw != w && cw != fw && cw != w->tqtopLevelWidget()) cw = fd->next(); if (cw != w) sendXEmbedMessage( window, XEMBED_FOCUS_PREV ); } // L0628: Qt should no longer process this event. return true; } } break; default: break; } // L0640: Application gets to see the events anyway. return false; } // L0650: This filter receives all XEvents in both the client and the embedder. // Most of it involves the embedded client (except L0660, L0671). static int qxembed_x11_event_filter( XEvent* e) { switch ( e->type ) { case XKeyPress: case XKeyRelease: { // L0660: This is for the embedding side (L1450). last_key_event = e->xkey; break; } case ClientMessage: if ( e->xclient.message_type == xembed ) { // L0670: This is where the XEmbed messages are // processed on the client side. Time msgtime = (Time) e->xclient.data.l[0]; long message = e->xclient.data.l[1]; long detail = e->xclient.data.l[2]; // L0671: Keep Qt message time up to date if ( msgtime > GET_QT_X_TIME() ) SET_QT_X_TIME(msgtime); TQWidget* w = TQT_TQWIDGET(TQWidget::find( e->xclient.window )); if ( !w ) break; switch ( message) { case XEMBED_EMBEDDED_NOTIFY: { // L0675: We just have been embedded into a XEMBED aware widget. TQTLWExtra *extra = ((QPublicWidget*)w->tqtopLevelWidget())->topData(); extra->embedded = 1; #ifdef USE_QT4 // [FIXME] printf("[FIXME] WId not set in kdelibs/tdeui/qxembed.cpp\n\r"); #else // USE_QT4 extra->parentWinId = e->xclient.data.l[3]; #endif // USE_QT4 w->tqtopLevelWidget()->show(); break; } case XEMBED_WINDOW_ACTIVATE: { // L0676: Embedding window becomes active. Send a fake XFocusIn // to convince Qt that we are active as well. Qt will send // us a focus notification (L0615) that we will intercept to // ensure that we have no Qt focus widget yet. The Qt focus // widget might later be set in L0680. XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xfocus.display = qt_xdisplay(); ev.xfocus.type = XFocusIn; ev.xfocus.window = w->tqtopLevelWidget()->winId(); ev.xfocus.mode = NotifyNormal; ev.xfocus.detail = NotifyAncestor; tqApp->x11ProcessEvent( &ev ); } break; case XEMBED_WINDOW_DEACTIVATE: { // L0677: Embedding window becomes inactive. Send a fake XFocusOut // event to convince Qt that we no longer are active. We will // receive extra Qt FocusOut events but we do not care. XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xfocus.display = qt_xdisplay(); ev.xfocus.type = XFocusOut; ev.xfocus.window = w->tqtopLevelWidget()->winId(); ev.xfocus.mode = NotifyNormal; ev.xfocus.detail = NotifyAncestor; tqApp->x11ProcessEvent( &ev ); } break; case XEMBED_FOCUS_IN: // L0680: Embedding application gives us the focus. { // L0681: Search saved focus widget. TQWidget* focusCurrent = 0; TQGuardedPtr* fw = focusMap->find( w->tqtopLevelWidget() ); if ( fw ) { focusCurrent = *fw; // L0682: Remove it from the map focusMap->remove( w->tqtopLevelWidget() ); } switch ( detail ) { case XEMBED_FOCUS_CURRENT: // L0683: Set focus on saved focus widget if ( focusCurrent ) { focusCurrent->setFocus(); if( QXEmbed* emb = tqt_dynamic_cast< QXEmbed* >( focusCurrent )) emb->updateEmbeddedFocus( true ); } else if ( !w->tqtopLevelWidget()->tqfocusWidget() ) w->tqtopLevelWidget()->setFocus(); break; case XEMBED_FOCUS_FIRST: { // L0684: Search first widget in tab chain #ifdef USE_QT4 w->tqtopLevelWidget()->setFocus(); ((QPublicWidget*)w->tqtopLevelWidget())->focusNextPrev(true); #else // USE_QT4 TQFocusEvent::setReason( TQFocusEvent::Tab ); w->tqtopLevelWidget()->setFocus(); ((QPublicWidget*)w->tqtopLevelWidget())->focusNextPrev(true); TQFocusEvent::resetReason(); #endif // USE_QT4 } break; case XEMBED_FOCUS_LAST: { // L0686: Search last widget in tab chain #ifdef USE_QT4 w->tqtopLevelWidget()->setFocus(); ((QPublicWidget*)w->tqtopLevelWidget())->focusNextPrev(false); #else // USE_QT4 TQFocusEvent::setReason( TQFocusEvent::Backtab ); w->tqtopLevelWidget()->setFocus(); ((QPublicWidget*)w->tqtopLevelWidget())->focusNextPrev(false); TQFocusEvent::resetReason(); #endif // USE_QT4 } break; default: break; } } break; case XEMBED_FOCUS_OUT: // L0688: Embedding application takes the focus away // We first record what the focus widget was // and clear the Qt focus. if ( w->tqtopLevelWidget()->tqfocusWidget() ) { if( QXEmbed* emb = tqt_dynamic_cast< QXEmbed* >( w->tqtopLevelWidget()->tqfocusWidget())) emb->updateEmbeddedFocus( false ); focusMap->insert( w->tqtopLevelWidget(), new TQGuardedPtr(w->tqtopLevelWidget()->tqfocusWidget() ) ); w->tqtopLevelWidget()->tqfocusWidget()->clearFocus(); } break; default: break; } } else if ( e->xclient.format == 32 && e->xclient.message_type ) { if ( e->xclient.message_type == qt_wm_protocols ) { TQWidget* w = TQT_TQWIDGET(TQWidget::find( e->xclient.window )); if ( !w ) break; // L0690: This is for the embedding side! // See L0902 for more information about the focus proxy. // Window manager may send WM_TAKE_FOCUS messages to the // embedding application to indicate that it becomes active. // But this also suggests that the window manager has // changed the X11 focus. We want to make sure it goes // to the focus proxy window eventually. Atom a = e->xclient.data.l[0]; if ( a == qt_wm_take_focus ) { // L0695: update Qt message time variable if ( (ulong) e->xclient.data.l[1] > GET_QT_X_TIME() ) SET_QT_X_TIME(e->xclient.data.l[1]); // L0696: There is no problem when the window is not active. // Qt will generate a WindowActivate event that will // do the job (L1310). This does not happen if the // window is already active. So we simulate it. if ( w->isActiveWindow() ) { TQEvent e( TQEvent::WindowActivate ); TQApplication::sendEvent( w, &e ); } } } } break; default: break; } // L0698: The next x11 filter if ( oldFilter ) return oldFilter( e ); // L0699: Otherwise process the event as usual. return false; } // L0700: Install the xembed filters in both client and embedder sides. // This function is called automatically when using // embedClientIntoWindow() or creating an instance of QXEmbed You may // have to call it manually for a client when using embedder-side // embedding, though. void QXEmbed::initialize() { static bool is_initialized = false; if ( is_initialized ) return; // L0710: Atom used by the XEMBED protocol. xembed = XInternAtom( qt_xdisplay(), "_XEMBED", false ); // L0720: Install low level filter for X11 events (L0650) oldFilter = qt_set_x11_event_filter( qxembed_x11_event_filter ); // L0730: See L0610 for an explanation about focusMap. focusMap = new TQPtrDict >; focusMap->setAutoDelete( true ); // L0740: Create client side application wide event filter (L0610) filter = new QXEmbedAppFilter; is_initialized = true; } // ------------------------------------------------------------ // L0800: MOST OF WHAT FOLLOWS CONCERNS THE EMBEDDER SIDE. // Things that happen inside a Qt application that contain // a QXEmbed widget for embedding other applications. // This applies to both the XEMBED and XPLAIN protocols. // Deviations are commented below. // L0810: Class QXEmbed. // A QXEmbed widget serves as an embedder that can manage one single // embedded X-window. These so-called client windows can be arbitrary // Qt or non Qt applications. There are two different ways of using // QXEmbed, from the client side or from the embedder's side. // L0900: Constructs a xembed widget. QXEmbed::QXEmbed(TQWidget *parent, const char *name, WFlags f) : TQWidget(parent, name, f) { // L0901: Create private data. See L0100. d = new QXEmbedData; // L0902: Create focus proxy widget. See XEmbed specification. // Each QXEmbed widget has a focus proxy window. Every single // QXEmbed widget tries to force its focus proxy window onto the // whole embedding application. They compete between themselves and // against Qt (L0690, L0914, L1040, L1310, L1510, L1580). // This would be much simpler if implemented within Qt. d->focusProxy = new TQWidget( tqtopLevelWidget(), "xembed_focus" ); d->focusProxy->setGeometry( -1, -1, 1, 1 ); d->focusProxy->show(); // make sure it's shown - for XSetInputFocus TQApplication::sendPostedEvents( d->focusProxy, 0 ); // L0903: Install the client side event filters // because they also provide services for the embedder side // See L0660, L0671, L0685. initialize(); window = 0; setFocusPolicy(TQ_StrongFocus); setKeyCompression( false ); // L0910: Trick Qt to create extraData(); (void) topData(); // L0912: We are mostly interested in SubstructureNotify // This is sent when something happens to the children of // the X11 window associated with the QXEmbed widget. XSelectInput(qt_xdisplay(), winId(), KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | KeymapStateMask | ButtonMotionMask | PointerMotionMask | // may need this, too EnterWindowMask | LeaveWindowMask | FocusChangeMask | ExposureMask | StructureNotifyMask | SubstructureRedirectMask | SubstructureNotifyMask ); // L0913: all application events pass through eventFilter(). // This is mostly used to force the X11 focus on the // proxy focus window. See L1300. tqtopLevelWidget()->installEventFilter( this ); tqApp->installEventFilter( this ); // L0914: Start moving the X11 focus on the focus proxy window. // See L1581 to know why we do not use isActiveWindow(). if ( tqApp->activeWindow() == tqtopLevelWidget() ) if ( !((QPublicWidget*) tqtopLevelWidget())->topData()->embedded ) XSetInputFocus( qt_xdisplay(), d->focusProxy->winId(), RevertToParent, GET_QT_X_TIME() ); // L0915: ??? [drag&drop?] setAcceptDrops( true ); } // L1000: Destructor must dispose of the embedded client window. QXEmbed::~QXEmbed() { // L1010: Make sure no pointer grab is left. if ( d && d->xgrab) XUngrabButton( qt_xdisplay(), AnyButton, AnyModifier, winId() ); if ( window && ( autoDelete() || !d->xplain )) { // L1021: Hide the window and safely reparent it into the root, // otherwise it would be destroyed by X11 together // with this QXEmbed's window. #if 0 // TODO: The proper XEmbed way would be to unmap the window, and the embedded // app would detect the embedding has ended, and do whatever it finds appropriate. // However, QXEmbed currently doesn't provide support for this detection, // so for the time being, it's better to leave the window mapped as toplevel window. // This will be ever more complicated with the systray windows, as the simple API // for them (KWin::setSystemTrayWindowFor()) doesn't make it possible to detect // themselves they have been released from systray, but KWin requires them // to be visible to allow next Kicker instance to swallow them. // See also below the L1022 comment. // XUnmapWindow( qt_xdisplay(), window ); #else if( autoDelete()) XUnmapWindow( qt_xdisplay(), window ); #endif XReparentWindow(qt_xdisplay(), window, qt_xrootwin(), 0, 0); if( !d->xplain ) XRemoveFromSaveSet( qt_xdisplay(), window ); if( d->mapAfterRelease ) XMapWindow( qt_xdisplay(), window ); XSync(qt_xdisplay(), false); // L1022: Send the WM_DELETE_WINDOW message if( autoDelete() /*&& d->xplain*/ ) // This sendDelete should only apply to XPLAIN. // XEMBED apps are supposed to detect when the embedding ends. // ??? [We do not do this detection yet! // So we sendDelete() instead.] sendDelete(); } window = 0; // L01040: Our focus proxy window will be destroyed as well. // Make sure that the X11 focus is not lost in the process. Window focus; int revert; XGetInputFocus( qt_xdisplay(), &focus, &revert ); if( focus == d->focusProxy->winId()) XSetInputFocus( qt_xdisplay(), tqtopLevelWidget()->winId(), RevertToParent, GET_QT_X_TIME() ); // L01045: Delete our private data. delete d; } // L1050: Sends a WM_DELETE_WINDOW message to the embedded window. This is // what typically happens when you click on the close button of a // window manager decoration. void QXEmbed::sendDelete( void ) { if (window) { sendClientMessage(window, qt_wm_protocols, qt_wm_delete_window); XFlush( qt_xdisplay() ); } } // L1100: Sets the protocol used for embedding windows. // This function must be called before embedding a window. // Protocol XEMBED provides maximal functionality (focus, tabs, etc) // but requires explicit cooperation from the embedded window. // Protocol XPLAIN provides maximal compatibility with // embedded applications that do not support the XEMBED protocol. // The default is XEMBED. void QXEmbed::setProtocol( Protocol proto ) { if (!window) { d->xplain = false; if (proto == XPLAIN) d->xplain = true; } } // L1150: Returns the protocol used for embedding the current window. QXEmbed::Protocol QXEmbed::protocol() { if (d->xplain) return XPLAIN; return XEMBED; } // L1200: QXEmbed widget size changes: resize embedded window. void QXEmbed::resizeEvent(TQResizeEvent*) { if (window) XResizeWindow(qt_xdisplay(), window, width(), height()); } // L1250: QXEmbed widget is shown: make sure embedded window is visible. void QXEmbed::showEvent(TQShowEvent*) { if (window) XMapRaised(qt_xdisplay(), window); } // L1300: This event filter sees all application events (L0913). bool QXEmbed::eventFilter( TQObject *o, TQEvent * e) { switch ( e->type() ) { case TQEvent::WindowActivate: if ( TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(tqtopLevelWidget()) ) { // L1310: Qt thinks the application window has just been activated. // Make sure the X11 focus is on the focus proxy window. See L0686. if ( !((QPublicWidget*) tqtopLevelWidget())->topData()->embedded ) if (! hasFocus() ) XSetInputFocus( qt_xdisplay(), d->focusProxy->winId(), RevertToParent, GET_QT_X_TIME() ); if (d->xplain) // L1311: Activation has changed. Grab state might change. See L2800. checkGrab(); else // L1312: Let the client know that we just became active sendXEmbedMessage( window, XEMBED_WINDOW_ACTIVATE ); } break; case TQEvent::WindowDeactivate: if ( TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(tqtopLevelWidget()) ) { if (d->xplain) // L1321: Activation has changed. Grab state might change. See L2800. checkGrab(); else // L1322: Let the client know that we are no longer active sendXEmbedMessage( window, XEMBED_WINDOW_DEACTIVATE ); } break; case TQEvent::Move: { TQWidget* pos = this; while( TQT_BASE_OBJECT(pos) != TQT_BASE_OBJECT(o) && TQT_BASE_OBJECT(pos) != TQT_BASE_OBJECT(tqtopLevelWidget())) pos = pos->parentWidget(); if( TQT_BASE_OBJECT(pos) == TQT_BASE_OBJECT(o) ) { // L1390: Send fake configure notify events whenever the // global position of the client changes. See L2900. TQPoint globalPos = mapToGlobal(TQPoint(0,0)); if (globalPos != d->lastPos) { d->lastPos = globalPos; sendSyntheticConfigureNotifyEvent(); } } } break; default: break; } return false; } // L1350: ??? [why this?] bool QXEmbed::event( TQEvent * e) { return TQWidget::event( e ); } // L1400: Forward keypress event to the client // Receiving a Qt key event indicates that // the QXEmbed object has the Qt focus. // The X11 event that caused the Qt key event // must be forwarded to the client. // See L0660. void QXEmbed::keyPressEvent( TQKeyEvent *) { if (!window) return; last_key_event.window = window; XSendEvent(qt_xdisplay(), window, false, KeyPressMask, (XEvent*)&last_key_event); } // L1450: Forward keyrelease event to the client. // See comment L1400. void QXEmbed::keyReleaseEvent( TQKeyEvent *) { if (!window) return; last_key_event.window = window; XSendEvent(qt_xdisplay(), window, false, KeyReleaseMask, (XEvent*)&last_key_event); } // L1500: Handle Qt focus in event. void QXEmbed::focusInEvent( TQFocusEvent * e ){ if (!window) return; // L1510: This is a good time to set the X11 focus on the focus proxy window. // Except if the the embedding application itself is embedded into another. if ( !((QPublicWidget*) tqtopLevelWidget())->topData()->embedded ) if ( tqApp->activeWindow() == tqtopLevelWidget() ) // L1511: Alter X focus only when window is active. // This is dual safety here because FocusIn implies this. // But see L1581 for an example where this really matters. XSetInputFocus( qt_xdisplay(), d->focusProxy->winId(), RevertToParent, GET_QT_X_TIME() ); if (d->xplain) { // L1520: Qt focus has changed. Grab state might change. See L2800. checkGrab(); // L1521: Window managers activate applications by setting the X11 focus. // We cannot do this (see L1510) but we can send a fake focus event // and forward the X11 key events ourselves (see L1400, L1450). sendFocusMessage(window, XFocusIn, NotifyNormal, NotifyPointer ); } else { // L1530: No need for fake events with XEMBED. // Just inform the client. It knows what to do. int detail = XEMBED_FOCUS_CURRENT; // L1531: When the focus change is caused by the tab key, // the client must select the first (or last) widget of // its own tab chain. if ( e->reason() == TQFocusEvent::Tab ) detail = XEMBED_FOCUS_FIRST; else if ( e->reason() == TQFocusEvent::Backtab ) detail = XEMBED_FOCUS_LAST; sendXEmbedMessage( window, XEMBED_FOCUS_IN, detail); } } // L1550: Handle Qt focus out event. void QXEmbed::focusOutEvent( TQFocusEvent * ){ if (!window) return; if (d->xplain) { // L1560: Qt focus has changed. Grab state might change. See L2800. checkGrab(); // L1561: Send fake focus out message. See L1521. sendFocusMessage(window, XFocusOut, NotifyNormal, NotifyPointer ); } else { // L1570: Send XEMBED focus out message. See L1531. sendXEmbedMessage( window, XEMBED_FOCUS_OUT ); } // L1580: The QXEmbed object might loose the focus because its // toplevel window looses the X11 focus and is no longer active, // or simply because the Qt focus has been moved to another widget. // In the latter case only, we want to make sure that the X11 focus // is properly set to the X11 focus widget. We do this because // the client application might have moved the X11 focus after // receiving the fake focus messages. if ( !((QPublicWidget*) tqtopLevelWidget())->topData()->embedded ) if ( tqApp->activeWindow() == tqtopLevelWidget() ) // L1581: Alter X focus only when window is active. // The test above is not the same as isActiveWindow(). // Function isActiveWindow() also returns true when a modal // dialog child of this window is active. XSetInputFocus( qt_xdisplay(), d->focusProxy->winId(), RevertToParent, GET_QT_X_TIME() ); } // When QXEmbed has TQt focus and gets/loses X focus, make sure the client knows // about the state of the focus. void QXEmbed::updateEmbeddedFocus( bool hasfocus ){ if (!window || d->xplain) return; if( hasfocus ) sendXEmbedMessage( window, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT); else sendXEmbedMessage( window, XEMBED_FOCUS_OUT); } // L1600: Helper for QXEmbed::embed() // Check whether a window is in withdrawn state. static bool wstate_withdrawn( WId winid ) { Atom type; int format; unsigned long length, after; unsigned char *data; int r = XGetWindowProperty( qt_xdisplay(), winid, qt_wm_state, 0, 2, false, AnyPropertyType, &type, &format, &length, &after, &data ); bool withdrawn = true; // L1610: Non managed windows have no WM_STATE property. // Returning true ensures that the loop L1711 stops. if ( r == Success && data && format == 32 ) { TQ_UINT32 *wstate = (TQ_UINT32*)data; withdrawn = (*wstate == WithdrawnState ); XFree( (char *)data ); } return withdrawn; } // L1650: Helper for QXEmbed::embed() // Get the X11 id of the parent window. static int get_parent(WId winid, Window *out_parent) { Window root, *children=0; unsigned int nchildren; int st = XQueryTree(qt_xdisplay(), winid, &root, out_parent, &children, &nchildren); if (st && children) XFree(children); return st; } // L1700: Embeds the window w into this QXEmbed widget. // See doc in qxembed.h. void QXEmbed::embed(WId w) { kdDebug() << "*** Embed " << w << " into " << winId() << ". window=" << window << endl; if (!w) return; // L1701: The has_window variable prevents embedding a same window twice. // ??? [what happens if one embed two windows into the same QXEmbed?] bool has_window = (w == window); window = w; if ( !has_window ) { KXErrorHandler errhandler; // make X BadWindow errors silent // L1710: Try hard to withdraw the window. // This makes sure that the window manager will // no longer try to manage this window. if ( !wstate_withdrawn(window) ) { XWithdrawWindow(qt_xdisplay(), window, qt_xscreen()); TQApplication::flushX(); // L1711: See L1610 for (int i=0; i < 10000; ++i) { if (wstate_withdrawn(window)) { Window parent = 0; get_parent(w, &parent); if (parent == qt_xrootwin()) break; } USLEEP(1000); } } // L1710: It would be sufficient in principle to reparent // window w into winId(). Everything else happens in L2020. // The following code might be useful when the X11 server takes // time to create the embedded application main window. Window parent = 0; get_parent(w, &parent); kdDebug() << TQString(TQString("> before reparent: parent=0x%1").arg(parent,0,16)) << endl; for (int i = 0; i < 50; i++) { // this is done once more when finishing embedding, but it's done also here // just in case we crash before reaching that place if( !d->xplain ) XAddToSaveSet( qt_xdisplay(), w ); XReparentWindow(qt_xdisplay(), w, winId(), 0, 0); if (get_parent(w, &parent) && parent == winId()) { kdDebug() << TQString(TQString("> Loop %1: ").arg(i)) << TQString(TQString("> reparent of 0x%1").arg(w,0,16)) << TQString(TQString(" into 0x%1").arg(winId(),0,16)) << TQString(" successful") << endl; break; } kdDebug() << TQString(TQString("> Loop %1: ").arg(i)) << TQString(TQString("> reparent of 0x%1").arg(w,0,16)) << TQString(TQString(" into 0x%1").arg(winId(),0,16)) << TQString(" failed") << endl; USLEEP(1000); } if( parent != winId()) // failed window = 0; } } // When a window is reparented into QXEmbed (or created inside of it), this function // sets up the actual embedding. void QXEmbed::handleEmbed() { // only XEMBED apps can survive crash, // see http://lists.kde.org/?l=kfm-devel&m=106752026501968&w=2 if( !d->xplain ) XAddToSaveSet( qt_xdisplay(), window ); XResizeWindow(qt_xdisplay(), window, width(), height()); XMapRaised(qt_xdisplay(), window); // L2024: see L2900. sendSyntheticConfigureNotifyEvent(); // L2025: ??? [any idea about drag&drop?] extraData()->xDndProxy = window; if ( parent() ) { // L2030: embedded window might have new size requirements. // see L2500, L2520, L2550. TQEvent * layoutHint = new TQEvent( TQEvent::LayoutHint ); TQApplication::postEvent( parent(), layoutHint ); } windowChanged( window ); if (d->xplain) { // L2040: Activation has changed. Grab state might change. See L2800. checkGrab(); if ( hasFocus() ) // L2041: Send fake focus message to inform the client. See L1521. sendFocusMessage(window, XFocusIn, NotifyNormal, NotifyPointer ); } else { // L2050: Send XEMBED messages (see L0670, L1312, L1322, L1530) sendXEmbedMessage( window, XEMBED_EMBEDDED_NOTIFY, 0, (long) winId() ); if (isActiveWindow()) sendXEmbedMessage( window, XEMBED_WINDOW_ACTIVATE); else sendXEmbedMessage( window, XEMBED_WINDOW_DEACTIVATE); if ( hasFocus() ) sendXEmbedMessage( window, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT ); } } // L1800: Returns the window identifier of the embedded window WId QXEmbed::embeddedWinId() const { return window; } // L1900: Control Qt tab focus management. // See Qt documentation. bool QXEmbed::focusNextPrevChild( bool next ) { if ( window ) // L1901: Return false when there is an embedded window // When the user presses TAB, Qt will not change // the focus and pass the TAB key events to the QXEmbed widget. // These key events will be forwarded to the client (L1400, L1450) // who eventually will manage the tab focus (L0620) and possible // instruct us to call TQWidget::focusNextPrevChild (L2081). return false; else // L1920: Default behavior otherwise. return TQWidget::focusNextPrevChild( next ); } // L2000: Filter for X11 events sent to the QXEmbed window. bool QXEmbed::x11Event( XEvent* e) { switch ( e->type ) { case DestroyNotify: if ( e->xdestroywindow.window == window ) { // L2005: Client window is being destroyed. window = 0; windowChanged( window ); emit embeddedWindowDestroyed(); } break; case CreateNotify: // A window was created inside of QXEmbed, handle it as embedded if( window == 0 ) { // only one window window = e->xcreatewindow.window; handleEmbed(); } break; case ReparentNotify: if ( e->xreparent.window == d->focusProxy->winId() ) break; // ignore proxy if ( window && e->xreparent.window == window && e->xreparent.parent != winId() ) { // L2010: We lost the window window = 0; windowChanged( window ); emit embeddedWindowDestroyed(); // L2011: Remove window from save set // ??? [not sure it is good to touch this window since // someone else has taken control of it already.] if( !d->xplain ) XRemoveFromSaveSet( qt_xdisplay(), window ); } else if ( e->xreparent.parent == winId()){ if( window == 0 ) // something started embedding from the outside window = e->xreparent.window; // L2020: We got a window. Complete the embedding process. if( e->xreparent.window == window ) handleEmbed(); } break; case ButtonPress: if (d->xplain && d->xgrab) { // L2060: The passive grab has intercepted a mouse click // in the embedded client window. Take the focus. #ifdef USE_QT4 setFocus(); #else // USE_QT4 TQFocusEvent::setReason( TQFocusEvent::Mouse ); setFocus(); TQFocusEvent::resetReason(); #endif // USE_QT4 // L2064: Resume X11 event processing. XAllowEvents(qt_xdisplay(), ReplayPointer, CurrentTime); // L2065: Qt should not know about this. return true; } break; case ButtonRelease: if (d->xplain && d->xgrab) { // L2064: Resume X11 event processing after passive grab (see L2060) XAllowEvents(qt_xdisplay(), SyncPointer, CurrentTime); return true; } break; case MapRequest: // L2070: Behave like a window manager. if ( window && e->xmaprequest.window == window ) XMapRaised(qt_xdisplay(), window ); break; case ClientMessage: // L2080: This is where the QXEmbed object receives XEMBED // messaged from the client application. if ( e->xclient.format == 32 && e->xclient.message_type == xembed ) { long message = e->xclient.data.l[1]; switch ( message ) { // L2081: Tab focus management. It is very important to call the // focusNextPrevChild() defined by TQWidget (not QXEmbed). // See L1901. case XEMBED_FOCUS_NEXT: TQWidget::focusNextPrevChild( true ); break; case XEMBED_FOCUS_PREV: TQWidget::focusNextPrevChild( false ); break; // L2085: The client asks for the focus. case XEMBED_REQUEST_FOCUS: if( ((QPublicWidget*)tqtopLevelWidget())->topData()->embedded ) { focusMap->remove( tqtopLevelWidget() ); focusMap->insert( tqtopLevelWidget(), new TQGuardedPtr( this )); #ifdef USE_QT4 WId window = ((QPublicWidget*)tqtopLevelWidget())->effectiveWinId(); #else // USE_QT4 WId window = ((QPublicWidget*)tqtopLevelWidget())->topData()->parentWinId; #endif // USE_QT4 sendXEmbedMessage( window, XEMBED_REQUEST_FOCUS ); } else { #ifdef USE_QT4 setFocus(); #else // USE_QT4 TQFocusEvent::setReason( TQFocusEvent::Mouse ); setFocus(); TQFocusEvent::resetReason(); #endif // USE_QT4 } break; default: break; } } break; case ConfigureRequest: // L2090: Client wants to change its geometry. // Just inform it that nothing has changed. if (e->xconfigurerequest.window == window) { sendSyntheticConfigureNotifyEvent(); } break; case MotionNotify: // fall through, workaround for Qt 3.0 < 3.0.3 case EnterNotify: // L2095: See L2200. if ( TQWhatsThis::inWhatsThisMode() ) enterWhatsThisMode(); break; default: break; } return false; } // L2200: Try to handle Qt's "what's this" mode. Broken. // "temporary, fix in Qt (Matthias, Mon Jul 17 15:20:55 CEST 2000" void QXEmbed::enterWhatsThisMode() { // L2210: When the what-s-this pointer enters the embedded window (L2095) // cancel what-s-this mode, and use a non stantard _NET_WM_ message // to instruct the embedded client to enter the "what's this" mode. // This works only one way... TQWhatsThis::leaveWhatsThisMode(); if ( !context_help ) context_help = XInternAtom( x11Display(), "_NET_WM_CONTEXT_HELP", false ); sendClientMessage(window , qt_wm_protocols, context_help ); } // L2300: indicates that the embedded window has been changed. void QXEmbed::windowChanged( WId ) { } // L2400: Utility function for clients that embed themselves. // This is client side code. bool QXEmbed::processClientCmdline( TQWidget* client, int& argc, char ** argv ) { int myargc = argc; WId window = 0; int i, j; j = 1; for ( i=1; iwinId(), window, 0, 0); // L2451: These two lines are redundant. See L0680. ((QXEmbed*)client)->topData()->embedded = true; #ifdef USE_QT4 // [FIXME] printf("[FIXME] WId not set in kdelibs/tdeui/qxembed.cpp\n\r"); #else // USE_QT4 ((QXEmbed*)client)->topData()->parentWinId = window; #endif // USE_QT4 // L2452: This seems redundant because L2020 maps the window. // But calling show() might also set Qt internal flags. client->show(); } // L2500: Specifies that this widget can use additional space, // and that it can survive on less than tqsizeHint(). TQSizePolicy QXEmbed::sizePolicy() const { return TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Expanding ); } // L2520: Returns a size sufficient for the embedded window TQSize QXEmbed::tqsizeHint() const { return tqminimumSizeHint(); } // L2550: Returns the minimum size specified by the embedded window. TQSize QXEmbed::tqminimumSizeHint() const { int minw = 0; int minh = 0; if ( window ) { XSizeHints size; long msize; if (XGetWMNormalHints(qt_xdisplay(), window, &size, &msize) && ( size.flags & PMinSize) ) { minw = size.min_width; minh = size.min_height; } } return TQSize( minw, minh ); } // L2600: Tells what shoud be done with the embedded window when // the embedding window is destroyed. void QXEmbed::setAutoDelete( bool b) { d->autoDelete = b; } // L2650: See L2600. bool QXEmbed::autoDelete() const { return d->autoDelete; } // L2700: See L2200. bool QXEmbed::customWhatsThis() const { return true; } // L2800: When using the XPLAIN protocol, this function maintains // a passive button grab when (1) the application is active // and (2) the Qt focus is not on the QXEmbed. This passive // grab intercepts button clicks in the client window and // give us chance to request the Qt focus (L2060). void QXEmbed::checkGrab() { if (d->xplain && isActiveWindow() && !hasFocus()) { if (! d->xgrab) XGrabButton(qt_xdisplay(), AnyButton, AnyModifier, winId(), false, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None ); d->xgrab = true; } else { if (d->xgrab) XUngrabButton( qt_xdisplay(), AnyButton, AnyModifier, winId() ); d->xgrab = false; } } // L2900: This sends fake configure notify events to inform // the client about its window geometry. See L1390, L2024 and L2090. void QXEmbed::sendSyntheticConfigureNotifyEvent() { // L2910: It seems that the x and y coordinates are global. // But this is what ICCCM section 4.1.5 wants. // See http://lists.kde.org/?l=kfm-devel&m=107090222032378 TQPoint globalPos = mapToGlobal(TQPoint(0,0)); if (window) { #if 0 XConfigureEvent c; memset(&c, 0, sizeof(c)); c.type = ConfigureNotify; c.display = qt_xdisplay(); c.send_event = True; c.event = window; c.window = window; c.x = globalPos.x(); c.y = globalPos.y(); c.width = width(); c.height = height(); c.border_width = 0; c.above = None; c.override_redirect = 0; XSendEvent( qt_xdisplay(), c.event, true, StructureNotifyMask, (XEvent*)&c ); #endif // Yes, this doesn't make sense at all. See the commit message. XSetWindowBorderWidth( qt_xdisplay(), window, 1 ); XSetWindowBorderWidth( qt_xdisplay(), window, 0 ); } } // L3000: One should not call TQWidget::reparent after embedding a window. void QXEmbed::reparent( TQWidget * parent, WFlags f, const TQPoint & p, bool showIt ) { // TQWidget::reparent() destroys the old X Window for the widget, and // creates a new one, thus QXEmbed after reparenting is no longer the // parent of the embedded window. I think reparenting of QXEmbed can be // done only by a mistake, so just complain. Q_ASSERT( !window ); TQWidget::reparent( parent, f, p, showIt ); } // for KDE #include "qxembed.moc" #endif // Q_WS_X11