qt-bugs@ issue : none yet Trolltech task ID : none bugs.kde.org number : none applied: no author: Lubos Lunak <l.lunak@kde.org> This patch adds Qt support for new window types used for compositing. --- src/gui/kernel/qwidget.h +++ src/gui/kernel/qwidget.h @@ -352,6 +352,19 @@ public: void setWindowOpacity(qreal level); qreal windowOpacity() const; +#if defined(Q_WS_X11) + enum X11WindowType { + X11WindowTypeSelect, + X11WindowTypeCombo, + X11WindowTypeDND, + X11WindowTypeTooltip, + X11WindowTypeMenu, // torn-off + X11WindowTypeDropdown, + X11WindowTypePopup + }; + void x11SetWindowType( X11WindowType type = X11WindowTypeSelect ); + void x11SetWindowTransient( QWidget* parent ); +#endif bool isWindowModified() const; #ifndef QT_NO_TOOLTIP --- src/gui/kernel/qdnd_x11.cpp +++ src/gui/kernel/qdnd_x11.cpp @@ -268,6 +268,7 @@ public: QWidget(QApplication::desktop()->screen(screen), Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint) { + x11SetWindowType( X11WindowTypeDND ); } void setPixmap(const QPixmap &pm) @@ -1436,6 +1437,7 @@ void QDragManager::move(const QPoint & g // recreate the pixmap on the new screen... delete xdnd_data.deco; xdnd_data.deco = new QShapedPixmapWidget(screen); + xdnd_data.deco->x11SetWindowTransient( object->source()->topLevelWidget()); if (!QWidget::mouseGrabber()) { updatePixmap(); xdnd_data.deco->grabMouse(); @@ -1897,6 +1899,7 @@ Qt::DropAction QDragManager::drag(QDrag object = o; object->d_func()->target = 0; xdnd_data.deco = new QShapedPixmapWidget(); + xdnd_data.deco->x11SetWindowTransient( object->source()->topLevelWidget()); willDrop = false; --- src/gui/kernel/qt_x11_p.h +++ src/gui/kernel/qt_x11_p.h @@ -528,6 +528,11 @@ struct QX11Data _NET_WM_WINDOW_TYPE_SPLASH, _NET_WM_WINDOW_TYPE_TOOLBAR, _NET_WM_WINDOW_TYPE_UTILITY, + _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + _NET_WM_WINDOW_TYPE_POPUP_MENU, + _NET_WM_WINDOW_TYPE_COMBO, + _NET_WM_WINDOW_TYPE_DND, + _NET_WM_WINDOW_TYPE_TOOLTIP, _KDE_NET_WM_FRAME_STRUT, --- src/gui/kernel/qtooltip.cpp +++ src/gui/kernel/qtooltip.cpp @@ -147,6 +147,9 @@ QTipLabel::QTipLabel(const QPoint &pos, setMouseTracking(true); fadingOut = false; reuseTip(text); +#ifdef Q_WS_X11 + x11SetWindowType( X11WindowTypeTooltip ); +#endif } void QTipLabel::restartHideTimer() @@ -367,6 +370,10 @@ void QToolTip::showText(const QPoint &po // that is showing (removes flickering) if (QTipLabel::instance->tipChanged(pos, text, w)){ QTipLabel::instance->reuseTip(text); +#ifdef Q_WS_X11 + if (w) + QTipLabel::instance->x11SetWindowTransient( w->topLevelWidget()); +#endif QTipLabel::instance->setTipRect(w, rect); QTipLabel::instance->placeTip(pos, w); } @@ -376,6 +383,10 @@ void QToolTip::showText(const QPoint &po if (!text.isEmpty()){ // no tip can be reused, create new tip: new QTipLabel(pos, text, w); // sets QTipLabel::instance to itself +#ifdef Q_WS_X11 + if (w) + QTipLabel::instance->x11SetWindowTransient( w->topLevelWidget()); +#endif QTipLabel::instance->setTipRect(w, rect); QTipLabel::instance->placeTip(pos, w); QTipLabel::instance->setObjectName(QLatin1String("qtooltip_label")); --- src/gui/kernel/qapplication_x11.cpp +++ src/gui/kernel/qapplication_x11.cpp @@ -212,6 +212,11 @@ static const char * x11_atomnames = { "_NET_WM_WINDOW_TYPE_SPLASH\0" "_NET_WM_WINDOW_TYPE_TOOLBAR\0" "_NET_WM_WINDOW_TYPE_UTILITY\0" + "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0" + "_NET_WM_WINDOW_TYPE_POPUP_MENU\0" + "_NET_WM_WINDOW_TYPE_COMBO\0" + "_NET_WM_WINDOW_TYPE_DND\0" + "_NET_WM_WINDOW_TYPE_TOOLTIP\0" "_KDE_NET_WM_FRAME_STRUT\0" --- src/gui/kernel/qwidget_x11.cpp +++ src/gui/kernel/qwidget_x11.cpp @@ -560,10 +560,6 @@ void QWidgetPrivate::create_sys(WId wind } #endif // QT_NO_XRENDER - // NET window types - long net_wintypes[7] = { 0, 0, 0, 0, 0, 0, 0 }; - int curr_wintype = 0; - QtMWMHints mwmhints; mwmhints.flags = 0L; mwmhints.functions = MWM_FUNC_ALL; @@ -574,15 +570,11 @@ void QWidgetPrivate::create_sys(WId wind if (topLevel) { ulong wsa_mask = 0; if (type == Qt::SplashScreen) { - net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_SPLASH); } else { // if (customize) { mwmhints.decorations = 0L; mwmhints.flags |= MWM_HINTS_DECORATIONS; - if (flags & Qt::FramelessWindowHint) { - // override netwm type - quick and easy for KDE noborder - net_wintypes[curr_wintype++] = ATOM(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE); - } else { + if ((flags & Qt::FramelessWindowHint) == 0 ) { mwmhints.decorations |= MWM_DECOR_BORDER; mwmhints.decorations |= MWM_DECOR_RESIZEH; @@ -604,22 +596,6 @@ void QWidgetPrivate::create_sys(WId wind wsa.save_under = True; wsa_mask |= CWSaveUnder; } - // ### need a better way to do this - if (q->inherits("QMenu")) { - // menu netwm type - net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_MENU); - } else if (q->inherits("QToolBar")) { - // toolbar netwm type - net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR); - } else if (type == Qt::Tool || type == Qt::Drawer) { - // utility netwm type - net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_UTILITY); - } - - if (dialog) // dialog netwm type - net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_DIALOG); - // normal netwm type - default - net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_NORMAL); if (flags & Qt::X11BypassWindowManagerHint) { wsa.override_redirect = True; @@ -638,6 +614,7 @@ void QWidgetPrivate::create_sys(WId wind wsa.save_under = True; XChangeWindowAttributes(dpy, id, CWOverrideRedirect | CWSaveUnder, &wsa); + q->x11SetWindowType(); } else if (topLevel && !desktop) { // top-level widget if (!X11->wm_client_leader) create_wm_client_leader(); @@ -682,12 +659,7 @@ void QWidgetPrivate::create_sys(WId wind // set mwm hints SetMWMHints(dpy, id, mwmhints); - // set _NET_WM_WINDOW_TYPE - if (curr_wintype > 0) - XChangeProperty(dpy, id, ATOM(_NET_WM_WINDOW_TYPE), XA_ATOM, 32, PropModeReplace, - (unsigned char *) net_wintypes, curr_wintype); - else - XDeleteProperty(dpy, id, ATOM(_NET_WM_WINDOW_TYPE)); + q->x11SetWindowType(); // set _NET_WM_WINDOW_TYPE // set _NET_WM_PID long curr_pid = getpid(); @@ -783,6 +755,63 @@ void QWidgetPrivate::create_sys(WId wind q->setAttribute(Qt::WA_OutsideWSRange, true); } +// Sets the EWMH (netwm) window type. Needed as a separate function +// because create() may be too soon in some cases. +void QWidget::x11SetWindowType( X11WindowType type ) +{ + Display* dpy = X11->display; + // NET window types + long net_wintypes[7] = { 0, 0, 0, 0, 0, 0, 0 }; + int curr_wintype = 0; + if( type == X11WindowTypeSelect ) { + if ( windowType() == Qt::SplashScreen ) { + net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_SPLASH); + } else if (inherits("QToolBar")) { + // toolbar netwm type + net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_TOOLBAR); + } else if (windowType() == Qt::Tool || windowType() == Qt::Drawer) { + // utility netwm type + net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_UTILITY); + } else if (windowType() == Qt::Dialog || windowType() == Qt::Sheet + || (windowFlags() & Qt::MSWindowsFixedSizeDialogHint)) { + // dialog netwm type + net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_DIALOG); + } + } else if( type == X11WindowTypeCombo ) { + // combo netwm type + net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_COMBO); + } else if( type == X11WindowTypeDND ) { + // dnd netwm type + net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_DND); + } else if( type == X11WindowTypeDropdown ) { + // dropdown netwm type + net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU); + } else if( type == X11WindowTypePopup ) { + // popup netwm type + net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_POPUP_MENU); + } else if( type == X11WindowTypeMenu ) { + // menu netwm type + net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_MENU); + } else if( type == X11WindowTypeTooltip ) { + // tooltip netwm type + net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_TOOLTIP); + } + + // normal netwm type - default + net_wintypes[curr_wintype++] = ATOM(_NET_WM_WINDOW_TYPE_NORMAL); + // set _NET_WM_WINDOW_TYPE + if (curr_wintype > 0) + XChangeProperty(dpy, winId(), ATOM(_NET_WM_WINDOW_TYPE), XA_ATOM, 32, PropModeReplace, + (unsigned char *) net_wintypes, curr_wintype); + else + XDeleteProperty(dpy, winId(), ATOM(_NET_WM_WINDOW_TYPE)); +} + +void QWidget::x11SetWindowTransient( QWidget* parent ) +{ + XSetTransientForHint( X11->display, winId(), parent->window()->winId()); +} + /*! Frees up window system resources. Destroys the widget window if \a destroyWindow is true. --- src/gui/widgets/qcombobox.cpp +++ src/gui/widgets/qcombobox.cpp @@ -332,6 +332,10 @@ void QComboBoxPrivateContainer::timerEve combo->update(); } } +#ifdef Q_WS_X11 + x11SetWindowType( X11WindowTypeCombo ); + x11SetWindowTransient( combo->topLevelWidget()); +#endif } void QComboBoxPrivateContainer::resizeEvent(QResizeEvent *e) --- src/gui/widgets/qmenu.cpp +++ src/gui/widgets/qmenu.cpp @@ -97,6 +97,9 @@ public: QTornOffMenu(QMenu *p) : QMenu(*(new QTornOffMenuPrivate(p))) { setParent(p, Qt::Window | Qt::Tool); +#ifdef Q_WS_X11 + x11SetWindowType( X11WindowTypeMenu ); +#endif setAttribute(Qt::WA_DeleteOnClose, true); setWindowTitle(p->windowTitle()); setEnabled(p->isEnabled()); @@ -143,6 +146,9 @@ void QMenuPrivate::init() } defaultMenuAction = menuAction = new QAction(q); menuAction->d_func()->menu = q; +#ifdef Q_WS_X11 + q->x11SetWindowType( QWidget::X11WindowTypePopup ); +#endif } //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't @@ -1678,6 +1684,33 @@ void QMenu::popup(const QPoint &p, QActi } } setGeometry(QRect(pos, size)); + +#ifdef Q_WS_X11 + QWidget* top = this; + for(;;) { + if( QMenu* m = qobject_cast< QMenu* >( top )) { + if( m->d_func()->causedPopup.widget == NULL ) + break; // ---> + top = m->d_func()->causedPopup.widget; + } else + break; // --> + } + if( QMenu* m = qobject_cast< QMenu* >( top )) + x11SetWindowType( X11WindowTypePopup ); + else + x11SetWindowType( X11WindowTypeDropdown ); + // hackish ... try to find the main window related to this popup + QWidget* parent = d_func()->causedPopup.widget; + if( parent == NULL ) + parent = parentWidget() ? parentWidget()->topLevelWidget() : NULL; + if( parent == NULL ) + parent = QApplication::widgetAt( pos ); + if( parent == NULL ) + parent = qApp->activeWindow(); + if( parent != NULL ) + x11SetWindowTransient( parent ); +#endif + #ifndef QT_NO_EFFECTS int hGuess = qApp->layoutDirection() == Qt::RightToLeft ? QEffects::LeftScroll : QEffects::RightScroll; int vGuess = QEffects::DownScroll; @@ -1874,6 +1907,9 @@ void QMenu::hideEvent(QHideEvent *) if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->causedPopup.widget)) mb->d_func()->setCurrentAction(0); #endif +#ifdef Q_WS_X11 + x11SetWindowType( X11WindowTypePopup ); // reset +#endif d->mouseDown = 0; d->hasHadMouse = false; d->causedPopup.widget = 0;