client.cpp

00001 /*****************************************************************
00002  KWin - the KDE window manager
00003  This file is part of the KDE project.
00004 
00005 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
00006 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
00007 
00008 You can Freely distribute this program under the GNU General Public
00009 License. See the file "COPYING" for the exact licensing terms.
00010 ******************************************************************/
00011 
00012 #include "client.h"
00013 
00014 #include <qapplication.h>
00015 #include <qpainter.h>
00016 #include <qdatetime.h>
00017 #include <kprocess.h>
00018 #include <unistd.h>
00019 #include <kstandarddirs.h>
00020 #include <qwhatsthis.h>
00021 #include <kwin.h>
00022 #include <kiconloader.h>
00023 #include <stdlib.h>
00024 
00025 #include "bridge.h"
00026 #include "group.h"
00027 #include "workspace.h"
00028 #include "atoms.h"
00029 #include "notifications.h"
00030 #include "rules.h"
00031 
00032 #include <X11/extensions/shape.h>
00033 
00034 // put all externs before the namespace statement to allow the linker
00035 // to resolve them properly
00036 
00037 extern Atom qt_wm_state;
00038 extern Time qt_x_time;
00039 extern Atom qt_window_role;
00040 extern Atom qt_sm_client_id;
00041 
00042 namespace KWinInternal
00043 {
00044 
00045 /*
00046 
00047  Creating a client:
00048      - only by calling Workspace::createClient()
00049          - it creates a new client and calls manage() for it
00050 
00051  Destroying a client:
00052      - destroyClient() - only when the window itself has been destroyed
00053      - releaseWindow() - the window is kept, only the client itself is destroyed
00054 
00055 */
00056 
00057 
00069 Client::Client( Workspace *ws )
00070     :   QObject( NULL ),
00071         client( None ),
00072         wrapper( None ),
00073         frame( None ),
00074         decoration( NULL ),
00075         wspace( ws ),
00076         bridge( new Bridge( this )),
00077         move_faked_activity( false ),
00078         move_resize_grab_window( None ),
00079         transient_for( NULL ),
00080         transient_for_id( None ),
00081         original_transient_for_id( None ),
00082         in_group( NULL ),
00083         window_group( None ),
00084         in_layer( UnknownLayer ),
00085         ping_timer( NULL ),
00086         process_killer( NULL ),
00087         user_time( CurrentTime ), // not known yet
00088         allowed_actions( 0 ),
00089         postpone_geometry_updates( 0 ),
00090         pending_geometry_update( false ),
00091         shade_geometry_change( false ),
00092         border_left( 0 ),
00093         border_right( 0 ),
00094         border_top( 0 ),
00095         border_bottom( 0 ),
00096         opacity_( 0 ),
00097         demandAttentionKNotifyTimer( NULL )
00098 // SELI do all as initialization
00099     {
00100     autoRaiseTimer = 0;
00101     shadeHoverTimer = 0;
00102 
00103     // set the initial mapping state
00104     mapping_state = WithdrawnState;
00105     desk = 0; // no desktop yet
00106 
00107     mode = PositionCenter;
00108     buttonDown = FALSE;
00109     moveResizeMode = FALSE;
00110 
00111     info = NULL;
00112 
00113     shade_mode = ShadeNone;
00114     active = FALSE;
00115     deleting = false;
00116     keep_above = FALSE;
00117     keep_below = FALSE;
00118     is_shape = FALSE;
00119     motif_noborder = false;
00120     motif_may_move = TRUE;
00121     motif_may_resize = TRUE;
00122     motif_may_close = TRUE;
00123     fullscreen_mode = FullScreenNone;
00124     skip_taskbar = FALSE;
00125     original_skip_taskbar = false;
00126     minimized = false;
00127     hidden = false;
00128     modal = false;
00129     noborder = false;
00130     user_noborder = false;
00131     urgency = false;
00132     ignore_focus_stealing = false;
00133     demands_attention = false;
00134     check_active_modal = false;
00135 
00136     Pdeletewindow = 0;
00137     Ptakefocus = 0;
00138     Ptakeactivity = 0;
00139     Pcontexthelp = 0;
00140     Pping = 0;
00141     input = FALSE;
00142     skip_pager = FALSE;
00143 
00144     max_mode = MaximizeRestore;
00145     maxmode_restore = MaximizeRestore;
00146     
00147     cmap = None;
00148     
00149     frame_geometry = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0)
00150     client_size = QSize( 100, 100 );
00151     custom_opacity = false;
00152     rule_opacity_active = 0;; //translucency rules
00153     rule_opacity_inactive = 0; //dito.
00154 
00155     // SELI initialize xsizehints??
00156     }
00157 
00161 Client::~Client()
00162     {
00163     assert(!moveResizeMode);
00164     assert( client == None );
00165     assert( frame == None && wrapper == None );
00166     assert( decoration == NULL );
00167     assert( postpone_geometry_updates == 0 );
00168     assert( !check_active_modal );
00169     delete info;
00170     delete bridge;
00171     }
00172 
00173 // use destroyClient() or releaseWindow(), Client instances cannot be deleted directly
00174 void Client::deleteClient( Client* c, allowed_t )
00175     {
00176     delete c;
00177     }
00178 
00182 void Client::releaseWindow( bool on_shutdown )
00183     {
00184     assert( !deleting );
00185     deleting = true;
00186     workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules
00187     StackingUpdatesBlocker blocker( workspace());
00188     if (!custom_opacity) setOpacity(FALSE);
00189     if (moveResizeMode)
00190        leaveMoveResize();
00191     finishWindowRules();
00192     ++postpone_geometry_updates;
00193     setMappingState( WithdrawnState );
00194     setModal( false ); // otherwise its mainwindow wouldn't get focus
00195     hidden = true; // so that it's not considered visible anymore (can't use hideClient(), it would set flags)
00196     if( !on_shutdown )
00197         workspace()->clientHidden( this );
00198     XUnmapWindow( qt_xdisplay(), frameId()); // destroying decoration would cause ugly visual effect
00199     destroyDecoration();
00200     cleanGrouping();
00201     if( !on_shutdown )
00202         {
00203         workspace()->removeClient( this, Allowed );
00204         // only when the window is being unmapped, not when closing down KWin
00205         // (NETWM sections 5.5,5.7)
00206         info->setDesktop( 0 );
00207         desk = 0;
00208         info->setState( 0, info->state()); // reset all state flags
00209         }
00210     XDeleteProperty( qt_xdisplay(), client, atoms->kde_net_wm_user_creation_time);
00211     XDeleteProperty( qt_xdisplay(), client, atoms->net_frame_extents );
00212     XDeleteProperty( qt_xdisplay(), client, atoms->kde_net_wm_frame_strut );
00213     XReparentWindow( qt_xdisplay(), client, workspace()->rootWin(), x(), y());
00214     XRemoveFromSaveSet( qt_xdisplay(), client );
00215     XSelectInput( qt_xdisplay(), client, NoEventMask );
00216     if( on_shutdown )
00217         { // map the window, so it can be found after another WM is started
00218         XMapWindow( qt_xdisplay(), client );
00219     // TODO preserve minimized, shaded etc. state?
00220         }
00221     else
00222         {
00223         // Make sure it's not mapped if the app unmapped it (#65279). The app
00224         // may do map+unmap before we initially map the window by calling rawShow() from manage().
00225         XUnmapWindow( qt_xdisplay(), client ); 
00226         }
00227     client = None;
00228     XDestroyWindow( qt_xdisplay(), wrapper );
00229     wrapper = None;
00230     XDestroyWindow( qt_xdisplay(), frame );
00231     frame = None;
00232     --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry
00233     deleteClient( this, Allowed );
00234     }
00235 
00236 // like releaseWindow(), but this one is called when the window has been already destroyed
00237 // (e.g. the application closed it)
00238 void Client::destroyClient()
00239     {
00240     assert( !deleting );
00241     deleting = true;
00242     workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules
00243     StackingUpdatesBlocker blocker( workspace());
00244     if (moveResizeMode)
00245        leaveMoveResize();
00246     finishWindowRules();
00247     ++postpone_geometry_updates;
00248     setModal( false );
00249     hidden = true; // so that it's not considered visible anymore
00250     workspace()->clientHidden( this );
00251     destroyDecoration();
00252     cleanGrouping();
00253     workspace()->removeClient( this, Allowed );
00254     client = None; // invalidate
00255     XDestroyWindow( qt_xdisplay(), wrapper );
00256     wrapper = None;
00257     XDestroyWindow( qt_xdisplay(), frame );
00258     frame = None;
00259     --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry
00260     deleteClient( this, Allowed );
00261     }
00262 
00263 void Client::updateDecoration( bool check_workspace_pos, bool force )
00264     {
00265     if( !force && (( decoration == NULL && noBorder())
00266                     || ( decoration != NULL && !noBorder())))
00267         return;
00268     bool do_show = false;
00269     postponeGeometryUpdates( true );
00270     if( force )
00271         destroyDecoration();
00272     if( !noBorder())
00273         {
00274         setMask( QRegion()); // reset shape mask
00275         decoration = workspace()->createDecoration( bridge );
00276         // TODO check decoration's minimum size?
00277         decoration->init();
00278         decoration->widget()->installEventFilter( this );
00279         XReparentWindow( qt_xdisplay(), decoration->widget()->winId(), frameId(), 0, 0 );
00280         decoration->widget()->lower();
00281         decoration->borders( border_left, border_right, border_top, border_bottom );
00282         options->onlyDecoTranslucent ?
00283             setDecoHashProperty(border_top, border_right, border_bottom, border_left):
00284             unsetDecoHashProperty();
00285         int save_workarea_diff_x = workarea_diff_x;
00286         int save_workarea_diff_y = workarea_diff_y;
00287         move( calculateGravitation( false ));
00288         plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00289         workarea_diff_x = save_workarea_diff_x;
00290         workarea_diff_y = save_workarea_diff_y;
00291         do_show = true;
00292         }
00293     else
00294         destroyDecoration();
00295     if( check_workspace_pos )
00296         checkWorkspacePosition();
00297     postponeGeometryUpdates( false );
00298     if( do_show )
00299         decoration->widget()->show();
00300     updateFrameExtents();
00301     }
00302 
00303 void Client::destroyDecoration()
00304     {
00305     if( decoration != NULL )
00306         {
00307         delete decoration;
00308         decoration = NULL;
00309         QPoint grav = calculateGravitation( true );
00310         border_left = border_right = border_top = border_bottom = 0;
00311         setMask( QRegion()); // reset shape mask
00312         int save_workarea_diff_x = workarea_diff_x;
00313         int save_workarea_diff_y = workarea_diff_y;
00314         plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00315         move( grav );
00316         workarea_diff_x = save_workarea_diff_x;
00317         workarea_diff_y = save_workarea_diff_y;
00318         }
00319     }
00320 
00321 void Client::checkBorderSizes()
00322     {
00323     if( decoration == NULL )
00324         return;
00325     int new_left, new_right, new_top, new_bottom;
00326     decoration->borders( new_left, new_right, new_top, new_bottom );
00327     if( new_left == border_left && new_right == border_right
00328         && new_top == border_top && new_bottom == border_bottom )
00329         return;
00330     GeometryUpdatesPostponer blocker( this );
00331     move( calculateGravitation( true ));
00332     border_left = new_left;
00333     border_right = new_right;
00334     border_top = new_top;
00335     border_bottom = new_bottom;
00336     if (border_left != new_left ||
00337         border_right != new_right ||
00338         border_top != new_top ||
00339         border_bottom != new_bottom)
00340     options->onlyDecoTranslucent ?
00341        setDecoHashProperty(new_top, new_right, new_bottom, new_left):
00342        unsetDecoHashProperty();
00343     move( calculateGravitation( false ));
00344     plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00345     checkWorkspacePosition();
00346     }
00347 
00348 void Client::detectNoBorder()
00349     {
00350     if( Shape::hasShape( window()))
00351         {
00352         noborder = true;
00353         return;
00354         }
00355     switch( windowType())
00356         {
00357         case NET::Desktop :
00358         case NET::Dock :
00359         case NET::TopMenu :
00360         case NET::Splash :
00361             noborder = true;
00362           break;
00363         case NET::Unknown :
00364         case NET::Normal :
00365         case NET::Toolbar :
00366         case NET::Menu :
00367         case NET::Dialog :
00368         case NET::Utility :
00369             noborder = false;
00370           break;
00371         default:
00372             assert( false );
00373         }
00374     // NET::Override is some strange beast without clear definition, usually
00375     // just meaning "noborder", so let's treat it only as such flag, and ignore it as
00376     // a window type otherwise (SUPPORTED_WINDOW_TYPES_MASK doesn't include it)
00377     if( info->windowType( SUPPORTED_WINDOW_TYPES_MASK | NET::OverrideMask ) == NET::Override )
00378         noborder = true;
00379     }
00380 
00381 void Client::detectShapable()
00382     {
00383     if( Shape::hasShape( window()))
00384         return;
00385     switch( windowType())
00386         {
00387         case NET::Desktop :
00388         case NET::Dock :
00389         case NET::TopMenu :
00390         case NET::Splash :
00391           break;
00392         case NET::Unknown :
00393         case NET::Normal :
00394         case NET::Toolbar :
00395         case NET::Menu :
00396         case NET::Dialog :
00397         case NET::Utility :
00398             setShapable(FALSE);
00399           break;
00400         default:
00401             assert( false );
00402         }
00403     }
00404 
00405 void Client::updateFrameExtents()
00406     {
00407     NETStrut strut;
00408     strut.left = border_left;
00409     strut.right = border_right;
00410     strut.top = border_top;
00411     strut.bottom = border_bottom;
00412     info->setFrameExtents( strut );
00413     }
00414 
00415 // Resizes the decoration, and makes sure the decoration widget gets resize event
00416 // even if the size hasn't changed. This is needed to make sure the decoration
00417 // re-layouts (e.g. when options()->moveResizeMaximizedWindows() changes,
00418 // the decoration may turn on/off some borders, but the actual size
00419 // of the decoration stays the same).
00420 void Client::resizeDecoration( const QSize& s )
00421     {
00422     if( decoration == NULL )
00423         return;
00424     QSize oldsize = decoration->widget()->size();
00425     decoration->resize( s );
00426     if( oldsize == s )
00427         {
00428         QResizeEvent e( s, oldsize );
00429         QApplication::sendEvent( decoration->widget(), &e );
00430         }
00431     }
00432 
00433 bool Client::noBorder() const
00434     {
00435     return noborder || isFullScreen() || user_noborder || motif_noborder;
00436     }
00437 
00438 bool Client::userCanSetNoBorder() const
00439     {
00440     return !noborder && !isFullScreen() && !isShade();
00441     }
00442 
00443 bool Client::isUserNoBorder() const
00444     {
00445     return user_noborder;
00446     }
00447 
00448 void Client::setUserNoBorder( bool set )
00449     {
00450     if( !userCanSetNoBorder())
00451         return;
00452     set = rules()->checkNoBorder( set );
00453     if( user_noborder == set )
00454         return;
00455     user_noborder = set;
00456     updateDecoration( true, false );
00457     updateWindowRules();
00458     }
00459 
00460 void Client::updateShape()
00461     {
00462     // workaround for #19644 - shaped windows shouldn't have decoration
00463     if( shape() && !noBorder()) 
00464         {
00465         noborder = true;
00466         updateDecoration( true );
00467         }
00468     if ( shape() )
00469         {
00470         XShapeCombineShape(qt_xdisplay(), frameId(), ShapeBounding,
00471                            clientPos().x(), clientPos().y(),
00472                            window(), ShapeBounding, ShapeSet);
00473         setShapable(TRUE);
00474         }
00475     // !shape() mask setting is done in setMask() when the decoration
00476     // calls it or when the decoration is created/destroyed
00477 
00478     if( Shape::version() >= 0x11 ) // 1.1, has input shape support
00479         { // There appears to be no way to find out if a window has input
00480           // shape set or not, so always propagate the input shape
00481           // (it's the same like the bounding shape by default).
00482           // Also, build the shape using a helper window, not directly
00483           // in the frame window, because the sequence set-shape-to-frame,
00484           // remove-shape-of-client, add-input-shape-of-client has the problem
00485           // that after the second step there's a hole in the input shape
00486           // until the real shape of the client is added and that can make
00487           // the window lose focus (which is a problem with mouse focus policies)
00488         static Window helper_window = None;
00489         if( helper_window == None )
00490             helper_window = XCreateSimpleWindow( qt_xdisplay(), qt_xrootwin(),
00491                 0, 0, 1, 1, 0, 0, 0 );
00492         XResizeWindow( qt_xdisplay(), helper_window, width(), height());
00493         XShapeCombineShape( qt_xdisplay(), helper_window, ShapeInput, 0, 0,
00494                            frameId(), ShapeBounding, ShapeSet );
00495         XShapeCombineShape( qt_xdisplay(), helper_window, ShapeInput,
00496                            clientPos().x(), clientPos().y(),
00497                            window(), ShapeBounding, ShapeSubtract );
00498         XShapeCombineShape( qt_xdisplay(), helper_window, ShapeInput,
00499                            clientPos().x(), clientPos().y(),
00500                            window(), ShapeInput, ShapeUnion );
00501         XShapeCombineShape( qt_xdisplay(), frameId(), ShapeInput, 0, 0,
00502                            helper_window, ShapeInput, ShapeSet );
00503         }
00504     }
00505 
00506 void Client::setMask( const QRegion& reg, int mode )
00507     {
00508     _mask = reg;
00509     if( reg.isNull())
00510         XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00511             None, ShapeSet );
00512     else if( mode == X::Unsorted )
00513         XShapeCombineRegion( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00514             reg.handle(), ShapeSet );
00515     else
00516         {
00517         QMemArray< QRect > rects = reg.rects();
00518         XRectangle* xrects = new XRectangle[ rects.count() ];
00519         for( unsigned int i = 0;
00520              i < rects.count();
00521              ++i )
00522             {
00523             xrects[ i ].x = rects[ i ].x();
00524             xrects[ i ].y = rects[ i ].y();
00525             xrects[ i ].width = rects[ i ].width();
00526             xrects[ i ].height = rects[ i ].height();
00527             }
00528         XShapeCombineRectangles( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00529             xrects, rects.count(), ShapeSet, mode );
00530         delete[] xrects;
00531         }
00532     updateShape();
00533     }
00534 
00535 QRegion Client::mask() const
00536     {
00537     if( _mask.isEmpty())
00538         return QRegion( 0, 0, width(), height());
00539     return _mask;
00540     }
00541     
00542 void Client::setShapable(bool b)
00543     {
00544     long tmp = b?1:0;
00545     XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shapable, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &tmp, 1L);
00546     }
00547 
00548 void Client::hideClient( bool hide )
00549     {
00550     if( hidden == hide )
00551         return;
00552     hidden = hide;
00553     updateVisibility();
00554     }
00555     
00556 /*
00557   Returns whether the window is minimizable or not
00558  */
00559 bool Client::isMinimizable() const
00560     {
00561     if( isSpecialWindow())
00562         return false;
00563     if( isTransient())
00564         { // #66868 - let other xmms windows be minimized when the mainwindow is minimized
00565         bool shown_mainwindow = false;
00566         ClientList mainclients = mainClients();
00567         for( ClientList::ConstIterator it = mainclients.begin();
00568              it != mainclients.end();
00569              ++it )
00570             {
00571             if( (*it)->isShown( true ))
00572                 shown_mainwindow = true;
00573             }
00574         if( !shown_mainwindow )
00575             return true;
00576         }
00577     // this is here because kicker's taskbar doesn't provide separate entries
00578     // for windows with an explicitly given parent
00579     // TODO perhaps this should be redone
00580     if( transientFor() != NULL )
00581         return false;
00582     if( !wantsTabFocus()) // SELI - NET::Utility? why wantsTabFocus() - skiptaskbar? ?
00583         return false;
00584     return true;
00585     }
00586 
00590 void Client::minimize( bool avoid_animation )
00591     {
00592     if ( !isMinimizable() || isMinimized())
00593         return;
00594 
00595     Notify::raise( Notify::Minimize );
00596 
00597     // SELI mainClients().isEmpty() ??? - and in unminimize() too
00598     if ( mainClients().isEmpty() && isOnCurrentDesktop() && isShown( true ) && !avoid_animation )
00599         animateMinimizeOrUnminimize( true ); // was visible or shaded
00600 
00601     minimized = true;
00602 
00603     updateVisibility();
00604     updateAllowedActions();
00605     workspace()->updateMinimizedOfTransients( this );
00606     updateWindowRules();
00607     workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast );
00608     }
00609 
00610 void Client::unminimize( bool avoid_animation )
00611     {
00612     if( !isMinimized())
00613         return;
00614 
00615     Notify::raise( Notify::UnMinimize );
00616     minimized = false;
00617     if( isOnCurrentDesktop() && isShown( true ))
00618         {
00619         if( mainClients().isEmpty() && !avoid_animation )
00620             animateMinimizeOrUnminimize( FALSE );
00621         }
00622     updateVisibility();
00623     updateAllowedActions();
00624     workspace()->updateMinimizedOfTransients( this );
00625     updateWindowRules();
00626     }
00627 
00628 extern bool         blockAnimation;
00629 
00630 void Client::animateMinimizeOrUnminimize( bool minimize )
00631     {
00632     if ( blockAnimation )
00633         return;
00634     if ( !options->animateMinimize )
00635         return;
00636 
00637     if( decoration != NULL && decoration->animateMinimize( minimize ))
00638         return; // decoration did it
00639 
00640     // the function is a bit tricky since it will ensure that an
00641     // animation action needs always the same time regardless of the
00642     // performance of the machine or the X-Server.
00643 
00644     float lf,rf,tf,bf,step;
00645 
00646     int speed = options->animateMinimizeSpeed;
00647     if ( speed > 10 )
00648         speed = 10;
00649     if ( speed < 0 )
00650         speed = 0;
00651 
00652     step = 40. * (11 - speed );
00653 
00654     NETRect r = info->iconGeometry();
00655     QRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height );
00656     if ( !icongeom.isValid() )
00657         return;
00658 
00659     QPixmap pm = animationPixmap( minimize ? width() : icongeom.width() );
00660 
00661     QRect before, after;
00662     if ( minimize ) 
00663         {
00664         before = QRect( x(), y(), width(), pm.height() );
00665         after = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
00666         }
00667     else 
00668         {
00669         before = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
00670         after = QRect( x(), y(), width(), pm.height() );
00671         }
00672 
00673     lf = (after.left() - before.left())/step;
00674     rf = (after.right() - before.right())/step;
00675     tf = (after.top() - before.top())/step;
00676     bf = (after.bottom() - before.bottom())/step;
00677 
00678     grabXServer();
00679 
00680     QRect area = before;
00681     QRect area2;
00682     QPixmap pm2;
00683 
00684     QTime t;
00685     t.start();
00686     float diff;
00687 
00688     QPainter p ( workspace()->desktopWidget() );
00689     bool need_to_clear = FALSE;
00690     QPixmap pm3;
00691     do 
00692         {
00693         if (area2 != area)
00694             {
00695             pm = animationPixmap( area.width() );
00696             pm2 = QPixmap::grabWindow( qt_xrootwin(), area.x(), area.y(), area.width(), area.height() );
00697             p.drawPixmap( area.x(), area.y(), pm );
00698             if ( need_to_clear ) 
00699                 {
00700                 p.drawPixmap( area2.x(), area2.y(), pm3 );
00701                 need_to_clear = FALSE;
00702                 }
00703             area2 = area;
00704             }
00705         XFlush(qt_xdisplay());
00706         XSync( qt_xdisplay(), FALSE );
00707         diff = t.elapsed();
00708         if (diff > step)
00709             diff = step;
00710         area.setLeft(before.left() + int(diff*lf));
00711         area.setRight(before.right() + int(diff*rf));
00712         area.setTop(before.top() + int(diff*tf));
00713         area.setBottom(before.bottom() + int(diff*bf));
00714         if (area2 != area ) 
00715             {
00716             if ( area2.intersects( area ) )
00717                 p.drawPixmap( area2.x(), area2.y(), pm2 );
00718             else 
00719                 { // no overlap, we can clear later to avoid flicker
00720                 pm3 = pm2;
00721                 need_to_clear = TRUE;
00722                 }
00723             }
00724         } while ( t.elapsed() < step);
00725     if (area2 == area || need_to_clear )
00726         p.drawPixmap( area2.x(), area2.y(), pm2 );
00727 
00728     p.end();
00729     ungrabXServer();
00730     }
00731 
00732 
00736 QPixmap Client::animationPixmap( int w )
00737     {
00738     QFont font = options->font(isActive());
00739     QFontMetrics fm( font );
00740     QPixmap pm( w, fm.lineSpacing() );
00741     pm.fill( options->color(Options::ColorTitleBar, isActive() || isMinimized() ) );
00742     QPainter p( &pm );
00743     p.setPen(options->color(Options::ColorFont, isActive() || isMinimized() ));
00744     p.setFont(options->font(isActive()));
00745     p.drawText( pm.rect(), AlignLeft|AlignVCenter|SingleLine, caption() );
00746     return pm;
00747     }
00748 
00749 
00750 bool Client::isShadeable() const
00751     {
00752     return !isSpecialWindow() && !noBorder();
00753     }
00754 
00755 void Client::setShade( ShadeMode mode )
00756     {
00757     if( !isShadeable())
00758         return;
00759     mode = rules()->checkShade( mode );
00760     if( shade_mode == mode )
00761         return;
00762     bool was_shade = isShade();
00763     ShadeMode was_shade_mode = shade_mode;
00764     shade_mode = mode;
00765     if( was_shade == isShade())
00766         {
00767         if( decoration != NULL ) // decoration may want to update after e.g. hover-shade changes
00768             decoration->shadeChange();
00769         return; // no real change in shaded state
00770         }
00771 
00772     if( shade_mode == ShadeNormal )
00773         {
00774         if ( isShown( true ) && isOnCurrentDesktop())
00775                 Notify::raise( Notify::ShadeUp );
00776         }
00777     else if( shade_mode == ShadeNone )
00778         {
00779         if( isShown( true ) && isOnCurrentDesktop())
00780                 Notify::raise( Notify::ShadeDown );
00781         }
00782 
00783     assert( decoration != NULL ); // noborder windows can't be shaded
00784     GeometryUpdatesPostponer blocker( this );
00785     // decorations may turn off some borders when shaded
00786     decoration->borders( border_left, border_right, border_top, border_bottom );
00787 
00788     int as = options->animateShade? 10 : 1;
00789 // TODO all this unmapping, resizing etc. feels too much duplicated from elsewhere
00790     if ( isShade()) 
00791         { // shade_mode == ShadeNormal
00792         // we're about to shade, texx xcompmgr to prepare
00793         long _shade = 1;
00794         XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L);
00795         // shade
00796         int h = height();
00797         shade_geometry_change = true;
00798         QSize s( sizeForClientSize( QSize( clientSize())));
00799         s.setHeight( border_top + border_bottom );
00800         XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
00801         XUnmapWindow( qt_xdisplay(), wrapper );
00802         XUnmapWindow( qt_xdisplay(), client );
00803         XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00804         //as we hid the unmap event, xcompmgr didn't recognize the client wid has vanished, so we'll extra inform it        
00805         //done xcompmgr workaround
00806 // FRAME       repaint( FALSE );
00807 //        bool wasStaticContents = testWFlags( WStaticContents );
00808 //        setWFlags( WStaticContents );
00809         int step = QMAX( 4, QABS( h - s.height() ) / as )+1;
00810         do 
00811             {
00812             h -= step;
00813             XResizeWindow( qt_xdisplay(), frameId(), s.width(), h );
00814             resizeDecoration( QSize( s.width(), h ));
00815             QApplication::syncX();
00816             } while ( h > s.height() + step );
00817 //        if ( !wasStaticContents )
00818 //            clearWFlags( WStaticContents );
00819         plainResize( s );
00820         shade_geometry_change = false;
00821         if( isActive())
00822             {
00823             if( was_shade_mode == ShadeHover )
00824                 workspace()->activateNextClient( this );
00825             else
00826                 workspace()->focusToNull();
00827             }
00828         // tell xcompmgr shade's done
00829         _shade = 2;
00830         XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L);    
00831         }
00832     else 
00833         {
00834         int h = height();
00835         shade_geometry_change = true;
00836         QSize s( sizeForClientSize( clientSize()));
00837 // FRAME       bool wasStaticContents = testWFlags( WStaticContents );
00838 //        setWFlags( WStaticContents );
00839         int step = QMAX( 4, QABS( h - s.height() ) / as )+1;
00840         do 
00841             {
00842             h += step;
00843             XResizeWindow( qt_xdisplay(), frameId(), s.width(), h );
00844             resizeDecoration( QSize( s.width(), h ));
00845             // assume a border
00846             // we do not have time to wait for X to send us paint events
00847 // FRAME           repaint( 0, h - step-5, width(), step+5, TRUE);
00848             QApplication::syncX();
00849             } while ( h < s.height() - step );
00850 //        if ( !wasStaticContents )
00851 //            clearWFlags( WStaticContents );
00852         shade_geometry_change = false;
00853         plainResize( s );
00854         if( shade_mode == ShadeHover || shade_mode == ShadeActivated )
00855             setActive( TRUE );
00856         XMapWindow( qt_xdisplay(), wrapperId());
00857         XMapWindow( qt_xdisplay(), window());
00858         XDeleteProperty (qt_xdisplay(), client, atoms->net_wm_window_shade);
00859         if ( isActive() )
00860             workspace()->requestFocus( this );
00861         }
00862     checkMaximizeGeometry();
00863     info->setState( isShade() ? NET::Shaded : 0, NET::Shaded );
00864     info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden );
00865     updateVisibility();
00866     updateAllowedActions();
00867     workspace()->updateMinimizedOfTransients( this );
00868     decoration->shadeChange();
00869     updateWindowRules();
00870     }
00871 
00872 void Client::shadeHover()
00873     {
00874     setShade( ShadeHover );
00875     cancelShadeHover();
00876     }
00877 
00878 void Client::cancelShadeHover()
00879     {
00880     delete shadeHoverTimer;
00881     shadeHoverTimer = 0;
00882     }
00883 
00884 void Client::toggleShade()
00885     {
00886     // if the mode is ShadeHover or ShadeActive, cancel shade too
00887     setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone );
00888     }
00889 
00890 void Client::updateVisibility()
00891     {
00892     if( deleting )
00893         return;
00894     bool show = true;
00895     if( hidden )
00896         {
00897         setMappingState( IconicState );
00898         info->setState( NET::Hidden, NET::Hidden );
00899         setSkipTaskbar( true, false ); // also hide from taskbar
00900         rawHide();
00901         show = false;
00902         }
00903     else
00904         {
00905         setSkipTaskbar( original_skip_taskbar, false );
00906         }
00907     if( minimized )
00908         {
00909         setMappingState( IconicState );
00910         info->setState( NET::Hidden, NET::Hidden );
00911         rawHide();
00912         show = false;
00913         }
00914     if( show )
00915         info->setState( 0, NET::Hidden );
00916     if( !isOnCurrentDesktop())
00917         {
00918         setMappingState( IconicState );
00919         rawHide();
00920         show = false;
00921         }
00922     if( show )
00923         {
00924         bool belongs_to_desktop = false;
00925         for( ClientList::ConstIterator it = group()->members().begin();
00926              it != group()->members().end();
00927              ++it )
00928             if( (*it)->isDesktop())
00929                 {
00930                 belongs_to_desktop = true;
00931                 break;
00932                 }
00933         if( !belongs_to_desktop && workspace()->showingDesktop())
00934             workspace()->resetShowingDesktop( true );
00935         if( isShade())
00936             setMappingState( IconicState );
00937         else
00938             setMappingState( NormalState );
00939         rawShow();
00940         }
00941     }
00942 
00947 void Client::setMappingState(int s)
00948     {
00949     assert( client != None );
00950     assert( !deleting || s == WithdrawnState );
00951     if( mapping_state == s )
00952         return;
00953     bool was_unmanaged = ( mapping_state == WithdrawnState );
00954     mapping_state = s;
00955     if( mapping_state == WithdrawnState )
00956         {
00957         XDeleteProperty( qt_xdisplay(), window(), qt_wm_state );
00958         return;
00959         }
00960     assert( s == NormalState || s == IconicState );
00961 
00962     unsigned long data[2];
00963     data[0] = (unsigned long) s;
00964     data[1] = (unsigned long) None;
00965     XChangeProperty(qt_xdisplay(), window(), qt_wm_state, qt_wm_state, 32,
00966         PropModeReplace, (unsigned char *)data, 2);
00967 
00968     if( was_unmanaged ) // manage() did postpone_geometry_updates = 1, now it's ok to finally set the geometry
00969         postponeGeometryUpdates( false );
00970     }
00971 
00976 void Client::rawShow()
00977     {
00978     if( decoration != NULL )
00979         decoration->widget()->show(); // not really necessary, but let it know the state
00980     XMapWindow( qt_xdisplay(), frame );
00981     if( !isShade())
00982         {
00983         XMapWindow( qt_xdisplay(), wrapper );
00984         XMapWindow( qt_xdisplay(), client );
00985         }
00986     }
00987 
00993 void Client::rawHide()
00994     {
00995 // Here it may look like a race condition, as some other client might try to unmap
00996 // the window between these two XSelectInput() calls. However, they're supposed to
00997 // use XWithdrawWindow(), which also sends a synthetic event to the root window,
00998 // which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify
00999 // will be missed is also very minimal, so I don't think it's needed to grab the server
01000 // here.
01001     XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
01002     XUnmapWindow( qt_xdisplay(), frame );
01003     XUnmapWindow( qt_xdisplay(), wrapper );
01004     XUnmapWindow( qt_xdisplay(), client );
01005     XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
01006     if( decoration != NULL )
01007         decoration->widget()->hide(); // not really necessary, but let it know the state
01008     workspace()->clientHidden( this );
01009     }
01010 
01011 void Client::sendClientMessage(Window w, Atom a, Atom protocol, long data1, long data2, long data3)
01012     {
01013     XEvent ev;
01014     long mask;
01015 
01016     memset(&ev, 0, sizeof(ev));
01017     ev.xclient.type = ClientMessage;
01018     ev.xclient.window = w;
01019     ev.xclient.message_type = a;
01020     ev.xclient.format = 32;
01021     ev.xclient.data.l[0] = protocol;
01022     ev.xclient.data.l[1] = qt_x_time;
01023     ev.xclient.data.l[2] = data1;
01024     ev.xclient.data.l[3] = data2;
01025     ev.xclient.data.l[4] = data3;
01026     mask = 0L;
01027     if (w == qt_xrootwin())
01028       mask = SubstructureRedirectMask;        /* magic! */
01029     XSendEvent(qt_xdisplay(), w, False, mask, &ev);
01030     }
01031 
01032 /*
01033   Returns whether the window may be closed (have a close button)
01034  */
01035 bool Client::isCloseable() const
01036     {
01037     return rules()->checkCloseable( motif_may_close && !isSpecialWindow());
01038     }
01039 
01044 void Client::closeWindow()
01045     {
01046     if( !isCloseable())
01047         return;
01048     // Update user time, because the window may create a confirming dialog.
01049     updateUserTime(); 
01050     if ( Pdeletewindow )
01051         {
01052         Notify::raise( Notify::Close );
01053         sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window);
01054         pingWindow();
01055         }
01056     else 
01057         {
01058         // client will not react on wm_delete_window. We have not choice
01059         // but destroy his connection to the XServer.
01060         killWindow();
01061         }
01062     }
01063 
01064 
01068 void Client::killWindow()
01069     {
01070     kdDebug( 1212 ) << "Client::killWindow():" << caption() << endl;
01071     // not sure if we need an Notify::Kill or not.. until then, use
01072     // Notify::Close
01073     Notify::raise( Notify::Close );
01074 
01075     if( isDialog())
01076         Notify::raise( Notify::TransDelete );
01077     if( isNormalWindow())
01078         Notify::raise( Notify::Delete );
01079     killProcess( false );
01080     // always kill this client at the server
01081     XKillClient(qt_xdisplay(), window() );
01082     destroyClient();
01083     }
01084 
01085 // send a ping to the window using _NET_WM_PING if possible
01086 // if it doesn't respond within a reasonable time, it will be
01087 // killed
01088 void Client::pingWindow()
01089     {
01090     if( !Pping )
01091         return; // can't ping :(
01092     if( options->killPingTimeout == 0 )
01093         return; // turned off
01094     if( ping_timer != NULL )
01095         return; // pinging already
01096     ping_timer = new QTimer( this );
01097     connect( ping_timer, SIGNAL( timeout()), SLOT( pingTimeout()));
01098     ping_timer->start( options->killPingTimeout, true );
01099     ping_timestamp = qt_x_time;
01100     workspace()->sendPingToWindow( window(), ping_timestamp );
01101     }
01102 
01103 void Client::gotPing( Time timestamp )
01104     {
01105     if( timestamp != ping_timestamp )
01106         return;
01107     delete ping_timer;
01108     ping_timer = NULL;
01109     if( process_killer != NULL )
01110         {
01111         process_killer->kill();
01112         delete process_killer;
01113         process_killer = NULL;
01114         }
01115     }
01116 
01117 void Client::pingTimeout()
01118     {
01119     kdDebug( 1212 ) << "Ping timeout:" << caption() << endl;
01120     delete ping_timer;
01121     ping_timer = NULL;
01122     killProcess( true, ping_timestamp );
01123     }
01124 
01125 void Client::killProcess( bool ask, Time timestamp )
01126     {
01127     if( process_killer != NULL )
01128         return;
01129     Q_ASSERT( !ask || timestamp != CurrentTime );
01130     QCString machine = wmClientMachine( true );
01131     pid_t pid = info->pid();
01132     if( pid <= 0 || machine.isEmpty()) // needed properties missing
01133         return;
01134     kdDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")" << endl;
01135     if( !ask )
01136         {
01137         if( machine != "localhost" )
01138             {
01139             KProcess proc;
01140             proc << "xon" << machine << "kill" << pid;
01141             proc.start( KProcess::DontCare );
01142             }
01143         else
01144             ::kill( pid, SIGTERM );
01145         }
01146     else
01147         { // SELI TODO handle the window created by handler specially (on top,urgent?)
01148         process_killer = new KProcess( this );
01149         *process_killer << KStandardDirs::findExe( "kwin_killer_helper" )
01150             << "--pid" << QCString().setNum( pid ) << "--hostname" << machine
01151             << "--windowname" << caption().utf8()
01152             << "--applicationname" << resourceClass()
01153             << "--wid" << QCString().setNum( window())
01154             << "--timestamp" << QCString().setNum( timestamp );
01155         connect