From a5e508590a941cd8ebc7e39b4bc9e0750c63626a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= Date: Tue, 13 Feb 2007 15:11:59 +0000 Subject: [PATCH] I have a strange feeling nobody will be bothered enough to spend time with non-composited minimize/shade animations. svn path=/branches/work/kwin_composite/; revision=633222 --- COMPOSITE_TODO | 6 +- client.cpp | 168 +---------------------------- client.h | 2 - events.cpp | 282 ++++++++++++++++++++++++++++++++++++++----------- 4 files changed, 225 insertions(+), 233 deletions(-) diff --git a/COMPOSITE_TODO b/COMPOSITE_TODO index 9b969246c9..3c7416b994 100644 --- a/COMPOSITE_TODO +++ b/COMPOSITE_TODO @@ -193,8 +193,10 @@ Effects TODO + minimize/shade effects - to replace the ones from KWin core - - Client::animateMinimizeOrUnminimize() - - Client::setShade() +/ - minimizing + - check support for it and the avoid_animation flag ++ - shading + - shading will probably need special support / zoom effect - enlarge a portion of the screen diff --git a/client.cpp b/client.cpp index 493ba93b65..b69485cf47 100644 --- a/client.cpp +++ b/client.cpp @@ -558,10 +558,6 @@ void Client::minimize( bool avoid_animation ) Notify::raise( Notify::Minimize ); - // SELI mainClients().isEmpty() ??? - and in unminimize() too - if ( mainClients().isEmpty() && isOnCurrentDesktop() && isShown( true ) && !avoid_animation ) - animateMinimizeOrUnminimize( true ); // was visible or shaded - minimized = true; updateVisibility(); @@ -569,7 +565,7 @@ void Client::minimize( bool avoid_animation ) workspace()->updateMinimizedOfTransients( this ); updateWindowRules(); workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast ); - if( effects ) + if( effects && !avoid_animation ) // TODO shouldn't it tell effects at least about the change? effects->windowMinimized( effectWindow()); } @@ -580,16 +576,11 @@ void Client::unminimize( bool avoid_animation ) Notify::raise( Notify::UnMinimize ); minimized = false; - if( isOnCurrentDesktop() && isShown( true )) - { - if( mainClients().isEmpty() && !avoid_animation ) - animateMinimizeOrUnminimize( false ); - } updateVisibility(); updateAllowedActions(); workspace()->updateMinimizedOfTransients( this ); updateWindowRules(); - if( effects ) + if( effects && !avoid_animation ) effects->windowUnminimized( effectWindow()); } @@ -613,130 +604,6 @@ QRect Client::iconGeometry() const } } -extern bool blockAnimation; - -void Client::animateMinimizeOrUnminimize( bool minimize ) - { -#ifdef __GNUC__ - #warning implement kwin animation -#endif - if ( 1 || blockAnimation ) - return; - if ( !options->animateMinimize ) - return; - - if( decoration != NULL && decoration->animateMinimize( minimize )) - return; // decoration did it - - // the function is a bit tricky since it will ensure that an - // animation action needs always the same time regardless of the - // performance of the machine or the X-Server. - - float lf,rf,tf,bf,step; - - int speed = options->animateMinimizeSpeed; - if ( speed > 10 ) - speed = 10; - if ( speed < 0 ) - speed = 0; - - step = 40. * (11 - speed ); - - QRect icongeom = iconGeometry(); - if ( !icongeom.isValid() ) - return; - - QPixmap pm = animationPixmap( minimize ? width() : icongeom.width() ); - - QRect before, after; - if ( minimize ) - { - before = QRect( x(), y(), width(), pm.height() ); - after = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); - } - else - { - before = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); - after = QRect( x(), y(), width(), pm.height() ); - } - - lf = (after.left() - before.left())/step; - rf = (after.right() - before.right())/step; - tf = (after.top() - before.top())/step; - bf = (after.bottom() - before.bottom())/step; - - grabXServer(); - - QRect area = before; - QRect area2; - QPixmap pm2; - - QTime t; - t.start(); - float diff; - - QPainter p ( workspace()->desktopWidget() ); - bool need_to_clear = false; - QPixmap pm3; - do - { - if (area2 != area) - { - pm = animationPixmap( area.width() ); - pm2 = QPixmap::grabWindow( rootWindow(), area.x(), area.y(), area.width(), area.height() ); - p.drawPixmap( area.x(), area.y(), pm ); - if ( need_to_clear ) - { - p.drawPixmap( area2.x(), area2.y(), pm3 ); - need_to_clear = false; - } - area2 = area; - } - XFlush(display()); - XSync( display(), false ); - diff = t.elapsed(); - if (diff > step) - diff = step; - area.setLeft(before.left() + int(diff*lf)); - area.setRight(before.right() + int(diff*rf)); - area.setTop(before.top() + int(diff*tf)); - area.setBottom(before.bottom() + int(diff*bf)); - if (area2 != area ) - { - if ( area2.intersects( area ) ) - p.drawPixmap( area2.x(), area2.y(), pm2 ); - else - { // no overlap, we can clear later to avoid flicker - pm3 = pm2; - need_to_clear = true; - } - } - } while ( t.elapsed() < step); - if (area2 == area || need_to_clear ) - p.drawPixmap( area2.x(), area2.y(), pm2 ); - - p.end(); - ungrabXServer(); - } - - -/*! - The pixmap shown during (un)minimalization animation - */ -QPixmap Client::animationPixmap( int w ) - { - QFont font = options->font(isActive()); - QFontMetrics fm( font ); - QPixmap pm( w, fm.lineSpacing() ); - pm.fill( options->color(Options::ColorTitleBar, isActive() || isMinimized() ) ); - QPainter p( &pm ); - p.setPen(options->color(Options::ColorFont, isActive() || isMinimized() )); - p.setFont(options->font(isActive())); - p.drawText( pm.rect(), Qt::AlignLeft|Qt::AlignVCenter|Qt::TextSingleLine, caption() ); - return pm; - } - - bool Client::isShadeable() const { return !isSpecialWindow() && !noBorder(); @@ -775,13 +642,11 @@ void Client::setShade( ShadeMode mode ) // decorations may turn off some borders when shaded decoration->borders( border_left, border_right, border_top, border_bottom ); - int as = options->animateShade? 10 : 1; // TODO all this unmapping, resizing etc. feels too much duplicated from elsewhere if ( isShade()) { // shade_mode == ShadeNormal workspace()->addRepaint( geometry()); // shade - int h = height(); shade_geometry_change = true; QSize s( sizeForClientSize( QSize( clientSize()))); s.setHeight( border_top + border_bottom ); @@ -789,19 +654,6 @@ void Client::setShade( ShadeMode mode ) XUnmapWindow( display(), wrapper ); XUnmapWindow( display(), client ); XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask ); -// FRAME repaint( false ); -// bool wasStaticContents = testWFlags( WStaticContents ); -// setWFlags( WStaticContents ); - int step = qMax( 4, QABS( h - s.height() ) / as )+1; - do - { - h -= step; - XResizeWindow( display(), frameId(), s.width(), h ); - resizeDecoration( QSize( s.width(), h )); - QApplication::syncX(); - } while ( h > s.height() + step ); -// if ( !wasStaticContents ) -// clearWFlags( WStaticContents ); plainResize( s ); shade_geometry_change = false; if( isActive()) @@ -814,24 +666,8 @@ void Client::setShade( ShadeMode mode ) } else { - int h = height(); shade_geometry_change = true; QSize s( sizeForClientSize( clientSize())); -// FRAME bool wasStaticContents = testWFlags( WStaticContents ); -// setWFlags( WStaticContents ); - int step = qMax( 4, QABS( h - s.height() ) / as )+1; - do - { - h += step; - XResizeWindow( display(), frameId(), s.width(), h ); - resizeDecoration( QSize( s.width(), h )); - // assume a border - // we do not have time to wait for X to send us paint events -// FRAME repaint( 0, h - step-5, width(), step+5, true); - QApplication::syncX(); - } while ( h < s.height() - step ); -// if ( !wasStaticContents ) -// clearWFlags( WStaticContents ); shade_geometry_change = false; plainResize( s ); if( shade_mode == ShadeHover || shade_mode == ShadeActivated ) diff --git a/client.h b/client.h index 9b135ef0a1..b842a3b1c0 100644 --- a/client.h +++ b/client.h @@ -273,8 +273,6 @@ class Client void setCursor( Position m ); void setCursor( const QCursor& c ); - void animateMinimizeOrUnminimize( bool minimize ); - QPixmap animationPixmap( int w ); // transparent stuff void drawbound( const QRect& geom ); void clearbound(); diff --git a/events.cpp b/events.cpp index c110f704db..966c448c1c 100644 --- a/events.cpp +++ b/events.cpp @@ -21,6 +21,9 @@ License. See the file "COPYING" for the exact licensing terms. #include "tabbox.h" #include "group.h" #include "rules.h" +#include "unmanaged.h" +#include "scene.h" +#include "effects.h" #include #include @@ -83,6 +86,10 @@ void WinInfo::changeState( unsigned long state, unsigned long mask ) m_client->setFullScreen( true, false ); } +void WinInfo::disable() + { + m_client = NULL; // only used when the object is passed to Deleted + } // **************************************** // RootInfo @@ -199,15 +206,10 @@ bool Workspace::workspaceEvent( XEvent * e ) XUngrabKeyboard( display(), xTime() ); } - if( e->type == PropertyNotify || e->type == ClientMessage ) + if ( e->type == PropertyNotify || e->type == ClientMessage ) { - unsigned long dirty[ NETRootInfo::PROPERTIES_SIZE ]; - rootInfo->event( e, dirty, NETRootInfo::PROPERTIES_SIZE ); - if( dirty[ NETRootInfo::PROTOCOLS ] & NET::DesktopNames ) - saveDesktopSettings(); - if( dirty[ NETRootInfo::PROTOCOLS2 ] & NET::WM2DesktopLayout ) - setDesktopLayout( rootInfo->desktopLayoutOrientation(), rootInfo->desktopLayoutColumnsRows().width(), - rootInfo->desktopLayoutColumnsRows().height(), rootInfo->desktopLayoutCorner()); + if ( netCheck( e ) ) + return true; } // events that should be handled before Clients can get them @@ -223,6 +225,8 @@ bool Workspace::workspaceEvent( XEvent * e ) tab_box->handleMouseEvent( e ); return true; } + if( effects && effects->checkInputWindowEvent( e )) + return true; break; case KeyPress: { @@ -267,6 +271,11 @@ bool Workspace::workspaceEvent( XEvent * e ) if( c->windowEvent( e )) return true; } + else if( Unmanaged* c = findUnmanaged( WindowMatchPredicate( e->xany.window ))) + { + if( c->windowEvent( e )) + return true; + } else { Window special = findSpecialEventWindow( e ); @@ -324,13 +333,8 @@ bool Workspace::workspaceEvent( XEvent * e ) } return true; } - return ( e->xunmap.event != e->xunmap.window ); // hide wm typical event from Qt } - case MapNotify: - - return ( e->xmap.event != e->xmap.window ); // hide wm typical event from Qt - case ReparentNotify: { //do not confuse Qt with these events. After all, _we_ are the @@ -380,6 +384,19 @@ bool Workspace::workspaceEvent( XEvent * e ) } break; } + case MapNotify: + { + if( e->xmap.override_redirect ) + { + Unmanaged* c = findUnmanaged( WindowMatchPredicate( e->xmap.window )); + if( c == NULL ) + c = createUnmanaged( e->xmap.window ); + if( c ) + return c->windowEvent( e ); + } + return ( e->xmap.event != e->xmap.window ); // hide wm typical event from Qt + } + case EnterNotify: { if ( QWhatsThis::inWhatsThisMode() ) @@ -456,7 +473,29 @@ bool Workspace::workspaceEvent( XEvent * e ) if( electricBorder( e )) return true; break; + case MappingNotify: + XRefreshKeyboardMapping( &e->xmapping ); + tab_box->updateKeyMapping(); + break; + case Expose: + if( e->xexpose.window == rootWindow() && compositing()) // root window needs repainting + addRepaint( e->xexpose.x, e->xexpose.y, e->xexpose.width, e->xexpose.height ); + break; default: + if( e->type == Extensions::randrNotifyEvent() && Extensions::randrAvailable() ) + { +#ifdef HAVE_XRANDR + XRRUpdateConfiguration( e ); +#endif + if( compositing() ) + { + // desktopResized() should take care of when the size or + // shape of the desktop has changed, but we also want to + // catch refresh rate changes + finishCompositing(); + QTimer::singleShot( 0, this, SLOT( setupCompositing() ) ); + } + } break; } return false; @@ -496,6 +535,20 @@ Window Workspace::findSpecialEventWindow( XEvent* e ) }; } +/*! + Handles client messages sent to the workspace + */ +bool Workspace::netCheck( XEvent* e ) + { + unsigned int dirty = rootInfo->event( e ); + + if ( dirty & NET::DesktopNames ) + saveDesktopSettings(); + + return dirty != 0; + } + + // **************************************** // Client // **************************************** @@ -508,6 +561,7 @@ bool Client::windowEvent( XEvent* e ) if( e->xany.window == window()) // avoid doing stuff on frame or wrapper { unsigned long dirty[ 2 ]; + double old_opacity = opacity(); info->event( e, dirty, 2 ); // pass through the NET stuff if ( ( dirty[ WinInfo::PROTOCOLS ] & NET::WMName ) != 0 ) @@ -538,6 +592,21 @@ bool Client::windowEvent( XEvent* e ) if( demandAttentionKNotifyTimer != NULL ) demandAttentionKNotify(); } + if( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2Opacity ) + { + if( compositing()) + { + addRepaintFull(); + scene->windowOpacityChanged( this ); + if( effects ) + effects->windowOpacityChanged( effectWindow(), old_opacity ); + } + else + { // forward to the frame if there's possibly another compositing manager running + NETWinInfo i( display(), frameId(), rootWindow(), 0 ); + i.setOpacity( info->opacity()); + } + } } // TODO move all focus handling stuff to separate file? @@ -618,15 +687,23 @@ bool Client::windowEvent( XEvent* e ) workspace()->updateColormap(); } break; + case VisibilityNotify: + visibilityNotifyEvent( &e->xvisibility ); + break; default: if( e->xany.window == window()) - { - if( e->type == Shape::shapeEvent() ) { - is_shape = Shape::hasShape( window()); // workaround for #19644 - updateShape(); + if( e->type == Extensions::shapeNotifyEvent() ) + { + detectShape( window()); // workaround for #19644 + updateShape(); + } + } + if( e->xany.window == frameId()) + { + if( e->type == Extensions::damageNotifyEvent()) + damageNotifyEvent( reinterpret_cast< XDamageNotifyEvent* >( e )); } - } break; } return true; // eat all events @@ -728,8 +805,6 @@ void Client::destroyNotifyEvent( XDestroyWindowEvent* e ) } -bool blockAnimation = false; - /*! Handles client messages for the client window */ @@ -742,14 +817,13 @@ void Client::clientMessageEvent( XClientMessageEvent* e ) { if( isTopMenu() && workspace()->managingTopMenus()) return; // kwin controls these - if( e->data.l[ 1 ] ) - blockAnimation = true; + bool avoid_animation = ( e->data.l[ 1 ] ); if( e->data.l[ 0 ] == IconicState ) minimize(); else if( e->data.l[ 0 ] == NormalState ) { // copied from mapRequest() if( isMinimized()) - unminimize(); + unminimize( avoid_animation ); if( isShade()) setShade( ShadeNone ); if( !isOnCurrentDesktop()) @@ -760,7 +834,6 @@ void Client::clientMessageEvent( XClientMessageEvent* e ) demandAttention(); } } - blockAnimation = false; } else if ( e->message_type == atoms->wm_change_state) { @@ -829,6 +902,7 @@ void Client::configureRequestEvent( XConfigureRequestEvent* e ) */ void Client::propertyNotifyEvent( XPropertyEvent* e ) { + Toplevel::propertyNotifyEvent( e ); if( e->window != window()) return; // ignore frame/wrapper switch ( e->atom ) @@ -852,10 +926,6 @@ void Client::propertyNotifyEvent( XPropertyEvent* e ) default: if ( e->atom == atoms->wm_protocols ) getWindowProtocols(); - else if (e->atom == atoms->wm_client_leader ) - getWmClientLeader(); - else if( e->atom == atoms->wm_window_role ) - window_role = staticWindowRole( window()); else if( e->atom == atoms->motif_wm_hints ) getMotifHints(); break; @@ -995,13 +1065,18 @@ void Client::ungrabButton( int modifier ) */ void Client::updateMouseGrab() { + if( workspace()->globalShortcutsDisabled()) + { + XUngrabButton( display(), AnyButton, AnyModifier, wrapperId()); + // keep grab for the simple click without modifiers if needed + if( !( !options->clickRaise || not_obscured )) + grabButton( None ); + return; + } if( isActive() && !workspace()->forcedGlobalMouseGrab()) // see Workspace::establishTabBoxGrab() { // remove the grab for no modifiers only if the window // is unobscured or if the user doesn't want click raise - // (it is unobscured if it the topmost in the unconstrained stacking order, i.e. it is - // the most recently raised window) - bool not_obscured = workspace()->topClientOnDesktop( workspace()->currentDesktop(), true, false ) == this; if( !options->clickRaise || not_obscured ) ungrabButton( None ); else @@ -1021,37 +1096,6 @@ void Client::updateMouseGrab() } } -int qtToX11Button( Qt::ButtonState button ) - { - if( button == Qt::LeftButton ) - return Button1; - else if( button == Qt::MidButton ) - return Button2; - else if( button == Qt::RightButton ) - return Button3; - return AnyButton; - } - -int qtToX11State( Qt::ButtonState buttons, Qt::KeyboardModifiers modifiers ) - { - int ret = 0; - if( buttons & Qt::LeftButton ) - ret |= Button1Mask; - if( buttons & Qt::MidButton ) - ret |= Button2Mask; - if( buttons & Qt::RightButton ) - ret |= Button3Mask; - if( modifiers & Qt::ShiftModifier ) - ret |= ShiftMask; - if( modifiers & Qt::ControlModifier ) - ret |= ControlMask; - if( modifiers & Qt::AltModifier ) - ret |= KKeyServer::modXAlt(); - if( modifiers & Qt::MetaModifier ) - ret |= KKeyServer::modXMeta(); - return ret; - } - // Qt propagates mouse events up the widget hierachy, which means events // for the decoration window cannot be (easily) intercepted as X11 events bool Client::eventFilter( QObject* o, QEvent* e ) @@ -1095,6 +1139,15 @@ bool Client::eventFilter( QObject* o, QEvent* e ) // on the decoration widget. if( ev->size() != size()) return true; + // HACK: Avoid decoration redraw delays. On resize Qt sets WA_WStateConfigPending + // which delays all painting until a matching ConfigureNotify event comes. + // But this process itself is the window manager, so it's not needed + // to wait for that event, the geometry is known. + // Note that if Qt in the future changes how this flag is handled and what it + // triggers then this may potentionally break things. See mainly QETWidget::translateConfigEvent(). + decoration->widget()->setAttribute( Qt::WA_WState_ConfigPending, false ); + decoration->widget()->update(); + return false; } return false; } @@ -1436,6 +1489,17 @@ void Client::focusOutEvent( XFocusOutEvent* e ) setActive( false ); } +void Client::visibilityNotifyEvent( XVisibilityEvent * e) + { + if( e->window != frameId()) + return; // care only about the whole frame + bool new_not_obscured = e->state == VisibilityUnobscured; + if( not_obscured == new_not_obscured ) + return; + not_obscured = new_not_obscured; + updateMouseGrab(); + } + // performs _NET_WM_MOVERESIZE void Client::NETMoveResize( int x_root, int y_root, NET::Direction direction ) { @@ -1497,7 +1561,7 @@ void Client::keyPressEvent( uint key_code ) bool is_alt = key_code & Qt::ALT; key_code = key_code & 0xffff; int delta = is_control?1:is_alt?32:8; - QPoint pos = QCursor::pos(); + QPoint pos = cursorPos(); switch ( key_code ) { case Qt::Key_Left: @@ -1530,6 +1594,98 @@ void Client::keyPressEvent( uint key_code ) QCursor::setPos( pos ); } +// **************************************** +// Unmanaged +// **************************************** + +bool Unmanaged::windowEvent( XEvent* e ) + { + double old_opacity = opacity(); + unsigned long dirty[ 2 ]; + info->event( e, dirty, 2 ); // pass through the NET stuff + if( dirty[ NETWinInfo::PROTOCOLS2 ] & NET::WM2Opacity ) + { + if( compositing()) + { + addRepaintFull(); + scene->windowOpacityChanged( this ); + if( effects ) + effects->windowOpacityChanged( effectWindow(), old_opacity ); + } + } + switch (e->type) + { + case UnmapNotify: + unmapNotifyEvent( &e->xunmap ); + break; + case MapNotify: + mapNotifyEvent( &e->xmap ); + break; + case ConfigureNotify: + configureNotifyEvent( &e->xconfigure ); + break; + case PropertyNotify: + propertyNotifyEvent( &e->xproperty ); + default: + { + if( e->type == Extensions::shapeNotifyEvent() ) + { + detectShape( window()); + if( compositing() ) + discardWindowPixmap(); + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); + } + if( e->type == Extensions::damageNotifyEvent()) + damageNotifyEvent( reinterpret_cast< XDamageNotifyEvent* >( e )); + break; + } + } + return false; // don't eat events, even our own unmanaged widgets are tracked + } + +void Unmanaged::mapNotifyEvent( XMapEvent* ) + { + } + +void Unmanaged::unmapNotifyEvent( XUnmapEvent* ) + { + release(); + } + +void Unmanaged::configureNotifyEvent( XConfigureEvent* e ) + { + if( effects ) + effects->checkInputWindowStacking(); // keep them on top + QRect newgeom( e->x, e->y, e->width, e->height ); + if( newgeom == geom ) + return; + workspace()->addRepaint( geometry()); // damage old area + geom = newgeom; + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); + discardWindowPixmap(); + } + +// **************************************** +// Toplevel +// **************************************** + +void Toplevel::propertyNotifyEvent( XPropertyEvent* e ) + { + if( e->window != window()) + return; // ignore frame/wrapper + switch ( e->atom ) + { + default: + if (e->atom == atoms->wm_client_leader ) + getWmClientLeader(); + else if( e->atom == atoms->wm_window_role ) + getWindowRole(); + break; + } + } + // **************************************** // Group // ****************************************