kwin Library API Documentation

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 
00031 #include <X11/extensions/shape.h>
00032 
00033 // put all externs before the namespace statement to allow the linker
00034 // to resolve them properly
00035 
00036 extern Atom qt_wm_state;
00037 extern Time qt_x_time;
00038 extern Atom qt_window_role;
00039 extern Atom qt_sm_client_id;
00040 
00041 namespace KWinInternal
00042 {
00043 
00044 /*
00045 
00046  Creating a client:
00047      - only by calling Workspace::createClient()
00048          - it creates a new client and calls manage() for it
00049 
00050  Destroying a client:
00051      - destroyClient() - only when the window itself has been destroyed
00052      - releaseWindow() - the window is kept, only the client itself is destroyed
00053 
00054 */
00055 
00056 
00068 Client::Client( Workspace *ws )
00069     :   QObject( NULL ),
00070         client( None ),
00071         wrapper( None ),
00072         frame( None ),
00073         decoration( NULL ),
00074         wspace( ws ),
00075         bridge( new Bridge( this )),
00076         move_faked_activity( false ),
00077         transient_for( NULL ),
00078         transient_for_id( None ),
00079         original_transient_for_id( None ),
00080         in_group( NULL ),
00081         window_group( None ),
00082         in_layer( UnknownLayer ),
00083         ping_timer( NULL ),
00084         process_killer( NULL ),
00085         user_time( CurrentTime ), // not known yet
00086         allowed_actions( 0 ),
00087         block_geometry( 0 ),
00088         shade_geometry_change( false ),
00089         border_left( 0 ),
00090         border_right( 0 ),
00091         border_top( 0 ),
00092         border_bottom( 0 )
00093 // SELI do all as initialization
00094     {
00095     autoRaiseTimer = 0;
00096     shadeHoverTimer = 0;
00097 
00098     // set the initial mapping state
00099     mapping_state = WithdrawnState;
00100     desk = 0; // no desktop yet
00101 
00102     mode = PositionCenter;
00103     buttonDown = FALSE;
00104     moveResizeMode = FALSE;
00105 
00106     info = NULL;
00107 
00108     shade_mode = ShadeNone;
00109     active = FALSE;
00110     keep_above = FALSE;
00111     keep_below = FALSE;
00112     is_shape = FALSE;
00113     motif_may_move = TRUE;
00114     motif_may_resize = TRUE;
00115     motif_may_close = TRUE;
00116     fullscreen_mode = FullScreenNone;
00117     skip_taskbar = FALSE;
00118     original_skip_taskbar = false;
00119     minimized = false;
00120     hidden = false;
00121     modal = false;
00122     noborder = false;
00123     user_noborder = false;
00124     not_obscured = false;
00125     urgency = false;
00126 
00127     Pdeletewindow = 0;
00128     Ptakefocus = 0;
00129     Pcontexthelp = 0;
00130     Pping = 0;
00131     input = FALSE;
00132     store_settings = FALSE;
00133     skip_pager = FALSE;
00134 
00135 
00136     max_mode = MaximizeRestore;
00137 
00138     cmap = None;
00139     
00140     frame_geometry = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0)
00141     client_size = QSize( 100, 100 );
00142 
00143     // SELI initialize xsizehints??
00144     }
00145 
00149 Client::~Client()
00150     {
00151     assert(!moveResizeMode);
00152     assert( client == None );
00153     assert( frame == None && wrapper == None );
00154     assert( decoration == NULL );
00155     assert( block_geometry == 0 );
00156     delete info;
00157     delete bridge;
00158     }
00159 
00160 // use destroyClient() or releaseWindow(), Client instances cannot be deleted directly
00161 void Client::deleteClient( Client* c, allowed_t )
00162     {
00163     delete c;
00164     }
00165 
00169 void Client::releaseWindow( bool on_shutdown )
00170     {
00171     if (moveResizeMode)
00172        leaveMoveResize();
00173     setModal( false ); // otherwise its mainwindow wouldn't get focus
00174     hidden = true; // so that it's not considered visible anymore (can't use hideClient(), it would set flags)
00175     if( !on_shutdown )
00176         workspace()->clientHidden( this );
00177     XUnmapWindow( qt_xdisplay(), frameId()); // destroying decoration would cause ugly visual effect
00178     destroyDecoration();
00179     cleanGrouping();
00180     if( !on_shutdown )
00181         {
00182         workspace()->removeClient( this, Allowed );
00183         // only when the window is being unmapped, not when closing down KWin
00184         // (NETWM sections 5.5,5.7)
00185         info->setDesktop( 0 );
00186         desk = 0;
00187         info->setState( 0, info->state()); // reset all state flags
00188         }
00189     XDeleteProperty( qt_xdisplay(),  client, atoms->kde_net_wm_user_creation_time);
00190     // TODO remove KDEFrameStrut property
00191     XReparentWindow( qt_xdisplay(), client, workspace()->rootWin(), x(), y());
00192     XRemoveFromSaveSet( qt_xdisplay(), client );
00193     XSelectInput( qt_xdisplay(), client, NoEventMask );
00194     if( on_shutdown )
00195         { // map the window, so it can be found after another WM is started
00196         XMapWindow( qt_xdisplay(), client );
00197     // TODO preserve minimized, shaded etc. state?
00198         }
00199     else
00200         {
00201         // Make sure it's not mapped if the app unmapped it (#65279). The app
00202         // may do map+unmap before we initially map the window by calling rawShow() from manage().
00203         XUnmapWindow( qt_xdisplay(), client ); 
00204         }
00205     setMappingState( WithdrawnState ); // after all is done, tell the app
00206     client = None;
00207     XDestroyWindow( qt_xdisplay(), wrapper );
00208     wrapper = None;
00209     XDestroyWindow( qt_xdisplay(), frame );
00210     frame = None;
00211     deleteClient( this, Allowed );
00212     }
00213 
00214 // like releaseWindow(), but this one is called when the window has been already destroyed
00215 // (e.g. the application closed it)
00216 void Client::destroyClient()
00217     {
00218     if (moveResizeMode)
00219        leaveMoveResize();
00220     ++block_geometry;
00221     setModal( false );
00222     hidden = true; // so that it's not considered visible anymore
00223     workspace()->clientHidden( this );
00224     destroyDecoration();
00225     cleanGrouping();
00226     workspace()->removeClient( this, Allowed );
00227     client = None; // invalidate
00228     XDestroyWindow( qt_xdisplay(), wrapper );
00229     wrapper = None;
00230     XDestroyWindow( qt_xdisplay(), frame );
00231     frame = None;
00232     --block_geometry;
00233     deleteClient( this, Allowed );
00234     }
00235 
00236 void Client::updateDecoration( bool check_workspace_pos, bool force )
00237     {
00238     if( !force && (( decoration == NULL && noBorder())
00239                     || ( decoration != NULL && !noBorder())))
00240         return;
00241     bool do_show = false;
00242     ++block_geometry;
00243     if( force )
00244         destroyDecoration();
00245     if( !noBorder())
00246         {
00247         decoration = workspace()->createDecoration( bridge );
00248         // TODO check decoration's minimum size?
00249         decoration->init();
00250         decoration->widget()->installEventFilter( this );
00251         XReparentWindow( qt_xdisplay(), decoration->widget()->winId(), frameId(), 0, 0 );
00252         decoration->widget()->lower();
00253         decoration->borders( border_left, border_right, border_top, border_bottom );
00254         int save_workarea_diff_x = workarea_diff_x;
00255         int save_workarea_diff_y = workarea_diff_y;
00256         move( calculateGravitation( false ));
00257         if( !isShade())
00258             plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00259         else
00260             plainResize( sizeForClientSize( QSize( clientSize().width(), 0 ), SizemodeShaded ), ForceGeometrySet );
00261         workarea_diff_x = save_workarea_diff_x;
00262         workarea_diff_y = save_workarea_diff_y;
00263         do_show = true;
00264         }
00265     else
00266         destroyDecoration();
00267     if( check_workspace_pos )
00268         checkWorkspacePosition();
00269     --block_geometry;
00270     setGeometry( geometry(), ForceGeometrySet );
00271     if( do_show )
00272         decoration->widget()->show();
00273     updateFrameStrut();
00274     }
00275 
00276 void Client::destroyDecoration()
00277     {
00278     if( decoration != NULL )
00279         {
00280         delete decoration;
00281         decoration = NULL;
00282         QPoint grav = calculateGravitation( true );
00283         border_left = border_right = border_top = border_bottom = 0;
00284         setMask( QRegion()); // reset shape mask
00285         int save_workarea_diff_x = workarea_diff_x;
00286         int save_workarea_diff_y = workarea_diff_y;
00287         if( !isShade())
00288             plainResize( clientSize(), ForceGeometrySet );
00289         else
00290             plainResize( QSize( clientSize().width(), 0 ), ForceGeometrySet );
00291         move( grav );
00292         workarea_diff_x = save_workarea_diff_x;
00293         workarea_diff_y = save_workarea_diff_y;
00294         }
00295     }
00296 
00297 void Client::checkBorderSizes()
00298     {
00299     if( decoration == NULL )
00300         return;
00301     int new_left, new_right, new_top, new_bottom;
00302     decoration->borders( new_left, new_right, new_top, new_bottom );
00303     if( new_left == border_left && new_right == border_right
00304         && new_top == border_top && new_bottom == border_bottom )
00305         return;
00306     ++block_geometry;
00307     move( calculateGravitation( true ));
00308     border_left = new_left;
00309     border_right = new_right;
00310     border_top = new_top;
00311     border_bottom = new_bottom;
00312     move( calculateGravitation( false ));
00313     plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00314     checkWorkspacePosition();
00315     --block_geometry;
00316     setGeometry( geometry(), ForceGeometrySet );
00317     }
00318 
00319 void Client::detectNoBorder()
00320     {
00321     if( Shape::hasShape( window()) || Motif::noBorder( window()))
00322         {
00323         noborder = true; // TODO for all window types?
00324         return;
00325         }
00326     switch( windowType())
00327         {
00328         case NET::Desktop :
00329         case NET::Dock :
00330         case NET::TopMenu :
00331         case NET::Override :
00332         case NET::Splash :
00333             noborder = true;
00334           break;
00335         case NET::Unknown :
00336         case NET::Normal :
00337         case NET::Toolbar :
00338         case NET::Menu :
00339         case NET::Dialog :
00340         case NET::Utility :
00341             noborder = false;
00342           break;
00343         default:
00344             assert( false );
00345         }
00346     }
00347 
00348 void Client::updateFrameStrut()
00349     {
00350 // TODO KDEFrameStrut je ale pitome jmeno
00351     NETStrut strut;
00352     strut.left = border_left;
00353     strut.right = border_right;
00354     strut.top = border_top;
00355     strut.bottom = border_bottom;
00356     info->setKDEFrameStrut( strut );
00357     }
00358 
00359 // Resizes the decoration, and makes sure the decoration widget gets resize event
00360 // even if the size hasn't changed. This is needed to make sure the decoration
00361 // re-layouts (e.g. when options()->moveResizeMaximizedWindows() changes,
00362 // the decoration may turn on/off some borders, but the actual size
00363 // of the decoration stays the same).
00364 void Client::resizeDecoration( const QSize& s )
00365     {
00366     if( decoration == NULL )
00367         return;
00368     QSize oldsize = decoration->widget()->size();
00369     decoration->resize( s );
00370     if( oldsize == s )
00371         {
00372         QResizeEvent e( s, oldsize );
00373         QApplication::sendEvent( decoration->widget(), &e );
00374         }
00375     }
00376 
00377 bool Client::noBorder() const
00378     {
00379     return noborder || isFullScreen() || user_noborder;
00380     }
00381 
00382 bool Client::userCanSetNoBorder() const
00383     {
00384     return !noborder && !isFullScreen() && !isShade();
00385     }
00386 
00387 bool Client::isUserNoBorder() const
00388     {
00389     return user_noborder;
00390     }
00391 
00392 void Client::setUserNoBorder( bool set )
00393     {
00394     if( !userCanSetNoBorder())
00395         return;
00396     if( user_noborder == set )
00397         return;
00398     user_noborder = set;
00399     updateDecoration( true, false );
00400     }
00401 
00402 void Client::updateShape()
00403     {
00404     if ( shape() )
00405         XShapeCombineShape(qt_xdisplay(), frameId(), ShapeBounding,
00406                            clientPos().x(), clientPos().y(),
00407                            window(), ShapeBounding, ShapeSet);
00408     else
00409         XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00410                            None, ShapeSet);
00411     // workaround for #19644 - shaped windows shouldn't have decoration
00412     if( shape() && !noBorder()) 
00413         {
00414         noborder = true;
00415         updateDecoration( true );
00416         }
00417     }
00418 
00419 void Client::setMask( const QRegion& reg, int mode )
00420     {
00421     _mask = reg;
00422     if( reg.isNull())
00423         XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00424             None, ShapeSet );
00425     else if( mode == X::Unsorted )
00426         XShapeCombineRegion( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00427             reg.handle(), ShapeSet );
00428     else
00429         {
00430         QMemArray< QRect > rects = reg.rects();
00431         XRectangle* xrects = new XRectangle[ rects.count() ];
00432         for( unsigned int i = 0;
00433              i < rects.count();
00434              ++i )
00435             {
00436             xrects[ i ].x = rects[ i ].x();
00437             xrects[ i ].y = rects[ i ].y();
00438             xrects[ i ].width = rects[ i ].width();
00439             xrects[ i ].height = rects[ i ].height();
00440             }
00441         XShapeCombineRectangles( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00442             xrects, rects.count(), ShapeSet, mode );
00443         delete[] xrects;
00444         }
00445     }
00446 
00447 QRegion Client::mask() const
00448     {
00449     if( _mask.isEmpty())
00450         return QRegion( 0, 0, width(), height());
00451     return _mask;
00452     }
00453 
00454 void Client::hideClient( bool hide )
00455     {
00456     if( hidden == hide )
00457         return;
00458     hidden = hide;
00459     info->setState( hidden ? NET::Hidden : 0, NET::Hidden );
00460     if( hidden )
00461         {
00462         setMappingState( IconicState );
00463         rawHide();
00464         setSkipTaskbar( true, false ); // also hide from taskbar
00465         }
00466     else // !hidden
00467         {
00468         setSkipTaskbar( original_skip_taskbar, false );
00469         if( isOnCurrentDesktop())
00470             {
00471             if( isShown( false ))
00472                 setMappingState( NormalState );
00473             rawShow(); // is either visible or shaded
00474             }
00475         }
00476     }
00477 
00478 /*
00479   Returns whether the window is minimizable or not
00480  */
00481 bool Client::isMinimizable() const
00482     {
00483     if( !wantsTabFocus() // SELI co NET::Utility? a proc wantsTabFocus() - skiptaskbar? ?
00484         || ( isSpecialWindow() && !isOverride()))
00485         return false;
00486     if( isTransient())
00487         { // transients may be minimized only if mainwindow is not shown
00488         ClientList mainclients = mainClients();
00489         for( ClientList::ConstIterator it = mainclients.begin();
00490              it != mainclients.end();
00491              ++it )
00492             if( (*it)->isShown( true ))
00493                 return false;
00494         }
00495     return true;
00496     }
00497 
00501 void Client::minimize()
00502     {
00503     if ( !isMinimizable() || isMinimized())
00504         return;
00505 
00506     minimized = true;
00507 
00508     Notify::raise( Notify::Minimize );
00509 
00510     // SELI mainClients().isEmpty() ??? - and in unminimize() too
00511     if ( mainClients().isEmpty() && isOnCurrentDesktop())
00512         animateMinimizeOrUnminimize( true ); // was visible or shaded
00513 
00514     setMappingState( IconicState );
00515     info->setState( NET::Hidden, NET::Hidden );
00516     rawHide();
00517     updateAllowedActions();
00518     workspace()->updateMinimizedOfTransients( this );
00519     }
00520 
00521 void Client::unminimize()
00522     {
00523     if( !isMinimized())
00524         return;
00525 
00526     Notify::raise( Notify::UnMinimize );
00527     minimized = false;    
00528     info->setState( 0, NET::Hidden );
00529     if( isOnCurrentDesktop())
00530         {
00531         if( mainClients().isEmpty())
00532             animateMinimizeOrUnminimize( FALSE );
00533         if( isShown( false ))
00534             setMappingState( NormalState );
00535         rawShow(); // is either visible or shaded
00536         }
00537     updateAllowedActions();
00538     workspace()->updateMinimizedOfTransients( this );
00539     }
00540 
00541 extern bool         blockAnimation;
00542 
00543 void Client::animateMinimizeOrUnminimize( bool minimize )
00544     {
00545     if ( blockAnimation )
00546         return;
00547     if ( !options->animateMinimize )
00548         return;
00549 
00550     if( decoration != NULL && decoration->animateMinimize( minimize ))
00551         return; // decoration did it
00552 
00553     // the function is a bit tricky since it will ensure that an
00554     // animation action needs always the same time regardless of the
00555     // performance of the machine or the X-Server.
00556 
00557     float lf,rf,tf,bf,step;
00558 
00559     int speed = options->animateMinimizeSpeed;
00560     if ( speed > 10 )
00561         speed = 10;
00562     if ( speed < 0 )
00563         speed = 0;
00564 
00565     step = 40. * (11 - speed );
00566 
00567     NETRect r = info->iconGeometry();
00568     QRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height );
00569     if ( !icongeom.isValid() )
00570         return;
00571 
00572     QPixmap pm = animationPixmap( minimize ? width() : icongeom.width() );
00573 
00574     QRect before, after;
00575     if ( minimize ) 
00576         {
00577         before = QRect( x(), y(), width(), pm.height() );
00578         after = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
00579         }
00580     else 
00581         {
00582         before = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
00583         after = QRect( x(), y(), width(), pm.height() );
00584         }
00585 
00586     lf = (after.left() - before.left())/step;
00587     rf = (after.right() - before.right())/step;
00588     tf = (after.top() - before.top())/step;
00589     bf = (after.bottom() - before.bottom())/step;
00590 
00591     grabXServer();
00592 
00593     QRect area = before;
00594     QRect area2;
00595     QPixmap pm2;
00596 
00597     QTime t;
00598     t.start();
00599     float diff;
00600 
00601     QPainter p ( workspace()->desktopWidget() );
00602     bool need_to_clear = FALSE;
00603     QPixmap pm3;
00604     do 
00605         {
00606         if (area2 != area)
00607             {
00608             pm = animationPixmap( area.width() );
00609             pm2 = QPixmap::grabWindow( qt_xrootwin(), area.x(), area.y(), area.width(), area.height() );
00610             p.drawPixmap( area.x(), area.y(), pm );
00611             if ( need_to_clear ) 
00612                 {
00613                 p.drawPixmap( area2.x(), area2.y(), pm3 );
00614                 need_to_clear = FALSE;
00615                 }
00616             area2 = area;
00617             }
00618         XFlush(qt_xdisplay());
00619         XSync( qt_xdisplay(), FALSE );
00620         diff = t.elapsed();
00621         if (diff > step)
00622             diff = step;
00623         area.setLeft(before.left() + int(diff*lf));
00624         area.setRight(before.right() + int(diff*rf));
00625         area.setTop(before.top() + int(diff*tf));
00626         area.setBottom(before.bottom() + int(diff*bf));
00627         if (area2 != area ) 
00628             {
00629             if ( area2.intersects( area ) )
00630                 p.drawPixmap( area2.x(), area2.y(), pm2 );
00631             else 
00632                 { // no overlap, we can clear later to avoid flicker
00633                 pm3 = pm2;
00634                 need_to_clear = TRUE;
00635                 }
00636             }
00637         } while ( t.elapsed() < step);
00638     if (area2 == area || need_to_clear )
00639         p.drawPixmap( area2.x(), area2.y(), pm2 );
00640 
00641     p.end();
00642     ungrabXServer();
00643     }
00644 
00645 
00649 QPixmap Client::animationPixmap( int w )
00650     {
00651     QFont font = options->font(isActive());
00652     QFontMetrics fm( font );
00653     QPixmap pm( w, fm.lineSpacing() );
00654     pm.fill( options->color(Options::ColorTitleBar, isActive() || isMinimized() ) );
00655     QPainter p( &pm );
00656     p.setPen(options->color(Options::ColorFont, isActive() || isMinimized() ));
00657     p.setFont(options->font(isActive()));
00658     p.drawText( pm.rect(), AlignLeft|AlignVCenter|SingleLine, caption() );
00659     return pm;
00660     }
00661 
00662 
00663 bool Client::isShadeable() const
00664     {
00665     return !isSpecialWindow() && !noBorder();
00666     }
00667 
00668 void Client::setShade( ShadeMode mode )
00669     {
00670     if( !isShadeable())
00671         return;
00672     if( shade_mode == mode )
00673         return;
00674     bool was_shade = isShade();
00675     ShadeMode was_shade_mode = shade_mode;
00676     shade_mode = mode;
00677     if( was_shade == isShade())
00678         return; // no real change in shaded state
00679 
00680     if( shade_mode == ShadeNormal )
00681         {
00682         if ( isShown( true ) && isOnCurrentDesktop())
00683                 Notify::raise( Notify::ShadeUp );
00684         }
00685     else if( shade_mode == ShadeNone )
00686         {
00687         if( isShown( true ) && isOnCurrentDesktop())
00688                 Notify::raise( Notify::ShadeDown );
00689         }
00690 
00691     assert( decoration != NULL ); // noborder windows can't be shaded
00692     ++block_geometry;
00693     // decorations may turn off some borders when shaded
00694     decoration->borders( border_left, border_right, border_top, border_bottom );
00695 
00696     int as = options->animateShade? 10 : 1;
00697 // TODO all this unmapping, resizing etc. feels too much duplicated from elsewhere
00698     if ( isShade()) 
00699         { // shade_mode == ShadeNormal
00700         int h = height();
00701         shade_geometry_change = true;
00702         QSize s( sizeForClientSize( QSize( clientSize().width(), 0), SizemodeShaded ) );
00703         XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
00704         XUnmapWindow( qt_xdisplay(), wrapper );
00705         XUnmapWindow( qt_xdisplay(), client );
00706         XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00707 // FRAME       repaint( FALSE );
00708 //        bool wasStaticContents = testWFlags( WStaticContents );
00709 //        setWFlags( WStaticContents );
00710         int step = QMAX( 4, QABS( h - s.height() ) / as )+1;
00711         do 
00712             {
00713             h -= step;
00714             XResizeWindow( qt_xdisplay(), frameId(), s.width(), h );
00715             resizeDecoration( QSize( s.width(), h ));
00716             QApplication::syncX();
00717             } while ( h > s.height() + step );
00718 //        if ( !wasStaticContents )
00719 //            clearWFlags( WStaticContents );
00720         shade_geometry_change = false;
00721         plainResize( s );
00722         if( isActive())
00723             {
00724             if( was_shade_mode == ShadeHover )
00725                 workspace()->activateNextClient( this );
00726             else
00727                 workspace()->focusToNull();
00728             }
00729         }
00730     else 
00731         {
00732         int h = height();
00733         shade_geometry_change = true;
00734         QSize s( sizeForClientSize( clientSize(), SizemodeShaded ));
00735 // FRAME       bool wasStaticContents = testWFlags( WStaticContents );
00736 //        setWFlags( WStaticContents );
00737         int step = QMAX( 4, QABS( h - s.height() ) / as )+1;
00738         do 
00739             {
00740             h += step;
00741             XResizeWindow( qt_xdisplay(), frameId(), s.width(), h );
00742             resizeDecoration( QSize( s.width(), h ));
00743             // assume a border
00744             // we do not have time to wait for X to send us paint events
00745 // FRAME           repaint( 0, h - step-5, width(), step+5, TRUE);
00746             QApplication::syncX();
00747             } while ( h < s.height() - step );
00748 //        if ( !wasStaticContents )
00749 //            clearWFlags( WStaticContents );
00750         shade_geometry_change = false;
00751         plainResize( s );
00752         if( shade_mode == ShadeHover || shade_mode == ShadeActivated )
00753             setActive( TRUE );
00754         XMapWindow( qt_xdisplay(), wrapperId());
00755         XMapWindow( qt_xdisplay(), window());
00756         if ( isActive() )
00757             workspace()->requestFocus( this );
00758         }
00759     --block_geometry;
00760     setGeometry( geometry(), ForceGeometrySet );
00761     info->setState( isShade() ? NET::Shaded : 0, NET::Shaded );
00762     info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden );
00763     setMappingState( isShown( false ) && isOnCurrentDesktop() ? NormalState : IconicState );
00764     updateAllowedActions();
00765     workspace()->updateMinimizedOfTransients( this );
00766     decoration->shadeChange();
00767     }
00768 
00769 void Client::shadeHover()
00770     {
00771     setShade( ShadeHover );
00772     delete shadeHoverTimer;
00773     shadeHoverTimer = 0;
00774     }
00775 
00776 void Client::toggleShade()
00777     {
00778     // if the mode is ShadeHover or ShadeActive, cancel shade too
00779     setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone );
00780     }
00781 
00782 void Client::virtualDesktopChange()
00783     {
00784     if( hidden || minimized )
00785         return; // no visibility change
00786     // from here it can be only shaded or normally shown
00787     if( isOnCurrentDesktop())
00788         {
00789         if( !isShade())
00790             setMappingState( NormalState );
00791         rawShow();
00792         }
00793     else
00794         {
00795         if( !isShade())
00796             setMappingState( IconicState );
00797         rawHide();
00798         }
00799     }
00800 
00805 void Client::setMappingState(int s)
00806     {
00807     assert( client != None );
00808     if( mapping_state == s )
00809         return;
00810     bool was_unmanaged = ( mapping_state == WithdrawnState );
00811     mapping_state = s;
00812     if( mapping_state == WithdrawnState )
00813         {
00814         XDeleteProperty( qt_xdisplay(), window(), qt_wm_state );
00815         return;
00816         }
00817     assert( s == NormalState || s == IconicState );
00818 
00819     unsigned long data[2];
00820     data[0] = (unsigned long) s;
00821     data[1] = (unsigned long) None;
00822     XChangeProperty(qt_xdisplay(), window(), qt_wm_state, qt_wm_state, 32,
00823         PropModeReplace, (unsigned char *)data, 2);
00824 
00825     if( was_unmanaged ) // force setting the geometry, manage() did block_geometry = 1
00826         {
00827         assert( block_geometry == 1 );
00828         --block_geometry;
00829         setGeometry( frame_geometry, ForceGeometrySet );
00830         }
00831     }
00832 
00837 void Client::rawShow()
00838     {
00839     if( decoration != NULL )
00840         decoration->widget()->show(); // not really necessary, but let it know the state
00841     XMapWindow( qt_xdisplay(), frame );
00842     if( !isShade())
00843         {
00844         XMapWindow( qt_xdisplay(), wrapper );
00845         XMapWindow( qt_xdisplay(), client );
00846         }
00847     }
00848 
00854 void Client::rawHide()
00855     {
00856 // Here it may look like a race condition, as some other client might try to unmap
00857 // the window between these two XSelectInput() calls. However, they're supposed to
00858 // use XWithdrawWindow(), which also sends a synthetic event to the root window,
00859 // which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify
00860 // will be missed is also very minimal, so I don't think it's needed to grab the server
00861 // here.
00862     XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
00863     XUnmapWindow( qt_xdisplay(), frame );
00864     XUnmapWindow( qt_xdisplay(), wrapper );
00865     XUnmapWindow( qt_xdisplay(), client );
00866     XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00867     if( decoration != NULL )
00868         decoration->widget()->hide(); // not really necessary, but let it know the state
00869     workspace()->clientHidden( this );
00870     }
00871 
00872 static void sendClientMessage(Window w, Atom a, long x)
00873     {
00874     XEvent ev;
00875     long mask;
00876 
00877     memset(&ev, 0, sizeof(ev));
00878     ev.xclient.type = ClientMessage;
00879     ev.xclient.window = w;
00880     ev.xclient.message_type = a;
00881     ev.xclient.format = 32;
00882     ev.xclient.data.l[0] = x;
00883     ev.xclient.data.l[1] = qt_x_time;
00884     mask = 0L;
00885     if (w == qt_xrootwin())
00886       mask = SubstructureRedirectMask;        /* magic! */
00887     XSendEvent(qt_xdisplay(), w, False, mask, &ev);
00888     }
00889 
00890 /*
00891   Returns whether the window may be closed (have a close button)
00892  */
00893 bool Client::isCloseable() const
00894     {
00895     return motif_may_close && ( !isSpecialWindow() || isOverride()); // TODO is NET::Override special?
00896     }
00897 
00902 void Client::closeWindow()
00903     {
00904     if( !isCloseable())
00905         return;
00906     // Update user time, needed for whole group, because the window may create a confirming dialog,
00907     // and this window's user time wouldn't apply to it
00908     // This is needed even for apps without support for user timestamp (e.g. nedit), so updating
00909     // user timestamp in apps on WM_DELETE_WINDOW is not an option (and I'm not sure if it would be right)
00910     group()->updateUserTime(); 
00911     if ( Pdeletewindow )
00912         {
00913         Notify::raise( Notify::Close );
00914         sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window);
00915         pingWindow();
00916         }
00917     else 
00918         {
00919         // client will not react on wm_delete_window. We have not choice
00920         // but destroy his connection to the XServer.
00921         killWindow();
00922         }
00923     }
00924 
00925 
00929 void Client::killWindow()
00930     {
00931     kdDebug( 1212 ) << "Client::killWindow():" << caption() << endl;
00932     // not sure if we need an Notify::Kill or not.. until then, use
00933     // Notify::Close
00934     Notify::raise( Notify::Close );
00935 
00936     if( isDialog())
00937         Notify::raise( Notify::TransDelete );
00938     if( isNormalWindow())
00939         Notify::raise( Notify::Delete );
00940     killProcess( false );
00941     // always kill this client at the server
00942     XKillClient(qt_xdisplay(), window() );
00943     destroyClient();
00944     }
00945 
00946 // send a ping to the window using _NET_WM_PING if possible
00947 // if it doesn't respond within a reasonable time, it will be
00948 // killed
00949 void Client::pingWindow()
00950     {
00951     if( !Pping )
00952         return; // can't ping :(
00953     if( ping_timer != NULL )
00954         return; // pinging already
00955     ping_timer = new QTimer( this );
00956     connect( ping_timer, SIGNAL( timeout()), SLOT( pingTimeout()));
00957     ping_timer->start( 5000, true ); // give it 5 seconds
00958     ping_timestamp = qt_x_time;
00959     workspace()->sendPingToWindow( window(), ping_timestamp );
00960     }
00961 
00962 void Client::gotPing( Time timestamp )
00963     {
00964     if( timestamp != ping_timestamp )
00965         return;
00966     delete ping_timer;
00967     ping_timer = NULL;
00968     if( process_killer != NULL )
00969         {
00970         process_killer->kill();
00971         delete process_killer;
00972         process_killer = NULL;
00973         }
00974     }
00975 
00976 void Client::pingTimeout()
00977     {
00978     kdDebug( 1212 ) << "Ping timeout:" << caption() << endl;
00979     delete ping_timer;
00980     ping_timer = NULL;
00981     killProcess( true, ping_timestamp );
00982     }
00983 
00984 void Client::killProcess( bool ask, Time timestamp )
00985     {
00986     if( process_killer != NULL )
00987         return;
00988     Q_ASSERT( !ask || timestamp != CurrentTime );
00989     QCString machine = wmClientMachine();
00990     pid_t pid = info->pid();
00991     if( pid <= 0 || machine.isEmpty()) // needed properties missing
00992         return;
00993     kdDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")" << endl;
00994     if( !ask )
00995         {
00996         if( machine != "localhost" )
00997             {
00998             KProcess proc;
00999             proc << "xon" << machine << "kill" << pid;
01000             proc.start( KProcess::DontCare );
01001             }
01002         else
01003             ::kill( pid, SIGTERM );
01004         }
01005     else
01006         { // SELI TODO handle the window created by handler specially (on top,urgent?)
01007         process_killer = new KProcess( this );
01008         *process_killer << KStandardDirs::findExe( "kwin_killer_helper" )
01009             << "--pid" << QCString().setNum( pid ) << "--hostname" << machine
01010             << "--windowname" << caption().utf8()
01011             << "--applicationname" << resourceClass()
01012             << "--wid" << QCString().setNum( window())
01013             << "--timestamp" << QCString().setNum( timestamp );
01014         connect( process_killer, SIGNAL( processExited( KProcess* )),
01015             SLOT( processKillerExited()));
01016         if( !process_killer->start( KProcess::NotifyOnExit ))
01017             {
01018             delete process_killer;
01019             process_killer = NULL;
01020             return;
01021             }
01022         }
01023     }
01024 
01025 void Client::processKillerExited()
01026     {
01027     kdDebug( 1212 ) << "Killer exited" << endl;
01028     delete process_killer;
01029     process_killer = NULL;
01030     }
01031 
01032 void Client::setSkipTaskbar( bool b, bool from_outside )
01033     {
01034     if( from_outside )
01035         original_skip_taskbar = b;
01036     if ( b == skipTaskbar() )
01037         return;
01038     skip_taskbar = b;
01039     info->setState( b?NET::SkipTaskbar:0, NET::SkipTaskbar );
01040     }
01041 
01042 void Client::setSkipPager( bool b )
01043     {
01044     if ( b == skipPager() )
01045         return;
01046     skip_pager = b;
01047     info->setState( b?NET::SkipPager:0, NET::SkipPager );
01048     }
01049 
01050 void Client::setModal( bool m )
01051     { // Qt-3.2 can have even modal normal windows :(
01052     if( modal == m )
01053         return;
01054     modal = m;
01055     if( !modal )
01056         return;
01057     // changing modality for a mapped window is weird (?)
01058     // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG
01059     }
01060 
01061 void Client::toggleOnAllDesktops()
01062     {
01063     setOnAllDesktops( !isOnAllDesktops());
01064     }
01065 
01066 void Client::setDesktop( int desktop )
01067     {
01068     if( desktop != NET::OnAllDesktops ) // do range check
01069         desktop = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desktop ));
01070     if( desk == desktop )
01071         return;
01072     int was_desk = desk;
01073     desk = desktop;
01074     info->setDesktop( desktop );
01075     if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops ))
01076         { // onAllDesktops changed
01077         if ( isShown( true ))
01078             Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops );
01079         workspace()->updateOnAllDesktopsOfTransients( this );
01080         }
01081     if( decoration != NULL )
01082         decoration->desktopChange();
01083     virtualDesktopChange(); // hide/show if needed
01084     }
01085 
01086 void Client::setOnAllDesktops( bool b )
01087     {
01088     if(( b && isOnAllDesktops())
01089         || ( !b && !isOnAllDesktops()))
01090         return;
01091     if( b )
01092         setDesktop( NET::OnAllDesktops );
01093     else
01094         setDesktop( workspace()->currentDesktop());
01095     }
01096 
01097 bool Client::isOnCurrentDesktop() const
01098     {
01099     return isOnDesktop( workspace()->currentDesktop());
01100     }
01101 
01106 void Client::takeFocus( bool force, allowed_t )
01107     {
01108     if ( !force && ( isTopMenu() || isDock() || isSplash()) )
01109         return; // toplevel menus and dock windows don't take focus if not forced
01110 
01111 #ifndef NDEBUG
01112     static Time previous_focus_timestamp;
01113     static Client* previous_client;
01114     if( previous_focus_timestamp == qt_x_time && previous_client != this )
01115         {
01116         kdWarning( 1212 ) << "Repeated use of the same X timestamp for focus" << endl;
01117         kdDebug( 1212 ) << kdBacktrace() << endl;
01118         }
01119     previous_focus_timestamp = qt_x_time;
01120     previous_client = this;
01121 #endif
01122     if ( input ) 
01123         {
01124         XSetInputFocus( qt_xdisplay(), window(), RevertToPointerRoot, qt_x_time );
01125         }
01126     if ( Ptakefocus )
01127         sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus);
01128     }
01129 
01137 bool Client::providesContextHelp() const
01138     {
01139     return Pcontexthelp;
01140     }
01141 
01142 
01149 void Client::showContextHelp()
01150     {
01151     if ( Pcontexthelp ) 
01152         {
01153         sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help);
01154         QWhatsThis::enterWhatsThisMode(); // SELI?
01155         }
01156     }
01157 
01158 
01163 KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption());
01164 
01165 void Client::fetchName()
01166     {
01167     QString s;
01168 
01169     if ( info->name() && info->name()[ 0 ] != '\0' ) 
01170         s = QString::fromUtf8( info->name() );
01171     else 
01172         s = KWin::readNameProperty( window(), XA_WM_NAME );
01173     if ( s != cap_normal ) 
01174         {
01175         bool reset_name = cap_normal.isEmpty();
01176         for( unsigned int i = 0;
01177              i < s.length();
01178              ++i )
01179             if( !s[ i ].isPrint())
01180                 s[ i ] = ' ';
01181         cap_normal = s;
01182         bool was_suffix = ( !cap_suffix.isEmpty());
01183         cap_suffix = QString::null;
01184         if ( ( !isSpecialWindow() || isToolbar()) && workspace()->findClient( FetchNameInternalPredicate( this ))) 
01185             {
01186             int i = 2;
01187             do 
01188                 {
01189                 cap_suffix = " <" + QString::number(i) + ">";
01190                 i++;
01191                 } while ( workspace()->findClient( FetchNameInternalPredicate( this )));
01192             info->setVisibleName( caption().utf8() );
01193             reset_name = false;
01194             }
01195         if(( was_suffix && cap_suffix.isEmpty()
01196             || reset_name )) // if it was new window, it may have old value still set, if the window is reused
01197             {
01198             info->setVisibleName( "" ); // remove
01199             info->setVisibleIconName( "" ); // remove
01200             }
01201         else if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set
01202             info->setVisibleIconName( ( cap_iconic + cap_suffix ).utf8() );
01203 
01204         if( isManaged() && decoration != NULL )
01205                 decoration->captionChange();
01206         }
01207     }
01208 
01209 void Client::fetchIconicName()
01210     {
01211     QString s;
01212 
01213     if ( info->iconName() && info->iconName()[ 0 ] != '\0' ) 
01214         s = QString::fromUtf8( info->iconName() );
01215     else 
01216         s = KWin::readNameProperty( window(), XA_WM_ICON_NAME );
01217     if ( s != cap_iconic ) 
01218         {
01219         cap_iconic = s;
01220         if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set
01221             info->setVisibleIconName( ( s + cap_suffix ).utf8() );
01222         }
01223     }
01224 
01227 QString Client::caption() const
01228     {
01229     return cap_normal + cap_suffix;
01230     }
01231 
01232 void Client::getWMHints()
01233     {
01234     XWMHints *hints = XGetWMHints(qt_xdisplay(), window() );
01235     input = true;
01236     window_group = None;
01237     urgency = false;
01238     if ( hints )
01239         {
01240         if( hints->flags & InputHint )
01241             input = hints->input;
01242         if( hints->flags & WindowGroupHint )
01243             window_group = hints->window_group;
01244         urgency = ( hints->flags & UrgencyHint ) ? true : false; // true/false needed, it's uint bitfield
01245         XFree( (char*)hints );
01246         }
01247     checkGroup();
01248     updateUrgency();
01249     updateAllowedActions(); // group affects isMinimizable()
01250     }
01251 
01252 void Client::readIcons( Window win, QPixmap* icon, QPixmap* miniicon )
01253     {    
01254     // get the icons, allow scaling
01255     if( icon != NULL )
01256         *icon = KWin::icon( win, 32, 32, TRUE, KWin::NETWM | KWin::WMHints );
01257     if( miniicon != NULL )
01258         if( icon == NULL || !icon->isNull())
01259             *miniicon = KWin::icon( win, 16, 16, TRUE, KWin::NETWM | KWin::WMHints );
01260         else
01261             *miniicon = QPixmap();
01262     }
01263 
01264 void Client::getIcons()
01265     {
01266     // first read icons from the window itself
01267     readIcons( window(), &icon_pix, &miniicon_pix );
01268     if( icon_pix.isNull())
01269         { // then try window group
01270         icon_pix = group()->icon();
01271         miniicon_pix = group()->miniIcon();
01272         }
01273     if( icon_pix.isNull() && isTransient())
01274         { // then mainclients
01275         ClientList mainclients = mainClients();
01276         for( ClientList::ConstIterator it = mainclients.begin();
01277              it != mainclients.end() && icon_pix.isNull();
01278              ++it )
01279             {
01280             icon_pix = (*it)->icon();
01281             miniicon_pix = (*it)->miniIcon();
01282             }
01283         }
01284     if( icon_pix.isNull())
01285         { // and if nothing else, load icon from classhint or xapp icon
01286         icon_pix = KWin::icon( window(), 32, 32, TRUE, KWin::ClassHint | KWin::XApp );
01287         miniicon_pix = KWin::icon( window(), 16, 16, TRUE, KWin::ClassHint | KWin::XApp );
01288         }
01289     if( isManaged() && decoration != NULL )
01290         decoration->iconChange();
01291     }
01292 
01293 void Client::getWindowProtocols()
01294     {
01295     Atom *p;
01296     int i,n;
01297 
01298     Pdeletewindow = 0;
01299     Ptakefocus = 0;
01300     Pcontexthelp = 0;
01301     Pping = 0;
01302 
01303     if (XGetWMProtocols(qt_xdisplay(), window(), &p, &n))
01304         {
01305         for (i = 0; i < n; i++)
01306             if (p[i] == atoms->wm_delete_window)
01307                 Pdeletewindow = 1;
01308             else if (p[i] == atoms->wm_take_focus)
01309                 Ptakefocus = 1;
01310             else if (p[i] == atoms->net_wm_context_help)
01311                 Pcontexthelp = 1;
01312             else if (p[i] == atoms->net_wm_ping)
01313                 Pping = 1;
01314         if (n>0)
01315             XFree(p);
01316         }
01317     }
01318 
01319 static int nullErrorHandler(Display *, XErrorEvent *)
01320     {
01321     return 0;
01322     }
01323 
01327 QCString Client::staticWindowRole(WId w)
01328     {
01329     return getStringProperty(w, qt_window_role);
01330     }
01331 
01335 QCString Client::staticSessionId(WId w)
01336     {
01337     return getStringProperty(w, qt_sm_client_id);
01338     }
01339 
01343 QCString Client::staticWmCommand(WId w)
01344     {
01345     return getStringProperty(w, XA_WM_COMMAND, ' ');
01346     }
01347 
01352 QCString Client::staticWmClientMachine(WId w)
01353     {
01354     QCString result = getStringProperty(w, XA_WM_CLIENT_MACHINE);
01355     if (result.isEmpty()) 
01356         {
01357         result = "localhost";
01358         }
01359     else 
01360         {
01361         // special name for the local machine (localhost)
01362         char hostnamebuf[80];
01363         if (gethostname (hostnamebuf, sizeof hostnamebuf) >= 0) 
01364             {
01365             hostnamebuf[sizeof(hostnamebuf)-1] = 0;
01366             if (result == hostnamebuf)
01367                 result = "localhost";
01368             char *dot = strchr(hostnamebuf, '.');
01369             if (dot && !(*dot = 0) && result == hostnamebuf)
01370                 result = "localhost";
01371             }
01372         }
01373     return result;
01374     }
01375 
01379 Window Client::staticWmClientLeader(WId w)
01380     {
01381     Atom type;
01382     int format, status;
01383     unsigned long nitems = 0;
01384     unsigned long extra = 0;
01385     unsigned char *data = 0;
01386     Window result = w;
01387     XErrorHandler oldHandler = XSetErrorHandler(nullErrorHandler);
01388     status = XGetWindowProperty( qt_xdisplay(), w, atoms->wm_client_leader, 0, 10000,
01389                                  FALSE, XA_WINDOW, &type, &format,
01390                                  &nitems, &extra, &data );
01391     XSetErrorHandler(oldHandler);
01392     if (status  == Success ) 
01393         {
01394         if (data && nitems > 0)
01395             result = *((Window*) data);
01396         XFree(data);
01397         }
01398     return result;
01399     }
01400 
01401 
01402 void Client::getWmClientLeader()
01403     {
01404     wmClientLeaderWin = staticWmClientLeader(window());
01405     }
01406 
01411 QCString Client::sessionId()
01412     {
01413     QCString result = staticSessionId(window());
01414     if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01415         result = staticSessionId(wmClientLeaderWin);
01416     return result;
01417     }
01418 
01423 QCString Client::wmCommand()
01424     {
01425     QCString result = staticWmCommand(window());
01426     if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01427         result = staticWmCommand(wmClientLeaderWin);
01428     return result;
01429     }
01430 
01435 QCString Client::wmClientMachine() const
01436     {
01437     QCString result = staticWmClientMachine(window());
01438     if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01439         result = staticWmClientMachine(wmClientLeaderWin);
01440     return result;
01441     }
01442 
01447 Window Client::wmClientLeader() const
01448     {
01449     if (wmClientLeaderWin)
01450         return wmClientLeaderWin;
01451     return window();
01452     }
01453 
01454 bool Client::wantsTabFocus() const
01455     {
01456     return ( isNormalWindow() || isDialog() || isOverride())
01457                           && ( input || Ptakefocus ) && !skip_taskbar;
01458     }
01459 
01460 
01461 bool Client::wantsInput() const
01462     {
01463     return input;
01464     }
01465 
01470 bool Client::isMovable() const
01471     {
01472     return motif_may_move && !isFullScreen() &&
01473         ( !isSpecialWindow() || isOverride() || isSplash() || isToolbar()) && // allow moving of splashscreens :)
01474         ( maximizeMode() != MaximizeFull || options->moveResizeMaximizedWindows() );
01475     }
01476 
01477 bool Client::isDesktop() const
01478     {
01479     return windowType() == NET::Desktop;
01480     }
01481 
01482 bool Client::isDock() const
01483     {
01484     return windowType() == NET::Dock;
01485     }
01486 
01487 bool Client::isTopMenu() const
01488     {
01489     return windowType() == NET::TopMenu;
01490     }
01491 
01492 
01493 bool Client::isMenu() const
01494     {
01495     return windowType() == NET::Menu && !isTopMenu(); // because of backwards comp.
01496     }
01497 
01498 bool Client::isToolbar() const
01499     {
01500     return windowType() == NET::Toolbar;
01501     }
01502 
01503 bool Client::isOverride() const
01504     {
01505     return windowType() == NET::Override;
01506     }
01507 
01508 bool Client::isSplash() const
01509     {
01510     return windowType() == NET::Splash;
01511     }
01512 
01513 bool Client::isUtility() const
01514     {
01515     return windowType() == NET::Utility;
01516     }
01517 
01518 bool Client::isDialog() const
01519     {
01520     return windowType() == NET::Dialog;
01521     }
01522 
01523 bool Client::isNormalWindow() const
01524     {
01525     return windowType() == NET::Normal;
01526     }
01527 
01528 bool Client::isSpecialWindow() const
01529     {
01530     return isDesktop() || isDock() || isSplash() || isTopMenu()
01531         || ( isOverride() && !isFullScreen())// SELI is NET::Override special or not?
01532         || isToolbar(); // TODO
01533     }
01534 
01535 NET::WindowType Client::windowType( bool strict, int supported_types ) const
01536     {
01537     NET::WindowType wt = info->windowType( supported_types );
01538     // TODO is this 'strict' really needed (and used?)
01539     if( !strict )
01540         { // hacks here
01541         if( wt == NET::Menu )
01542             {
01543             // ugly hack to support the times when NET::Menu meant NET::TopMenu
01544             // if it's as wide as the screen, not very high and has its upper-left
01545             // corner a bit above the screen's upper-left cornet, it's a topmenu
01546             if( x() == 0 && y() < 0 && y() > -10 && height() < 100
01547                 && abs( width() - workspace()->clientArea( FullArea, this ).width()) < 10 )
01548                 wt = NET::TopMenu;
01549             }
01550         const char* const oo_prefix = "openoffice.org"; // QCString has no startsWith()
01551         // oo_prefix is lowercase, because resourceClass() is forced to be lowercase
01552         if( qstrncmp( resourceClass(), oo_prefix, strlen( oo_prefix )) == 0 && wt == NET::Dialog )
01553             wt = NET::Normal; // see bug #66065
01554         if( wt == NET::Unknown ) // this is more or less suggested in NETWM spec
01555             wt = isTransient() ? NET::Dialog : NET::Normal;
01556         }
01557     return wt;
01558     }
01559 
01564 void Client::setCursor( Position m )
01565     {
01566     if ( !isResizable() || isShade() || noBorder())
01567         {
01568         setCursor( arrowCursor );
01569         return;
01570         }
01571     switch ( m ) 
01572         {
01573         case PositionTopLeft:
01574         case PositionBottomRight:
01575             setCursor( sizeFDiagCursor );
01576             break;
01577         case PositionBottomLeft:
01578         case PositionTopRight:
01579             setCursor( sizeBDiagCursor );
01580             break;
01581         case PositionTop:
01582         case PositionBottom:
01583             setCursor( sizeVerCursor );
01584             break;
01585         case PositionLeft:
01586         case PositionRight:
01587             setCursor( sizeHorCursor );
01588             break;
01589         default:
01590             setCursor( arrowCursor );
01591             break;
01592         }
01593     }
01594 
01595 // TODO mit nejake checkCursor(), ktere se zavola v manage() a pri vecech, kdy by se kurzor mohl zmenit?
01596 void Client::setCursor( const QCursor& c )
01597     {
01598     if( c.handle() == cursor.handle())
01599         return;
01600     cursor = c;
01601     if( decoration != NULL )
01602         decoration->widget()->setCursor( cursor );
01603     XDefineCursor( qt_xdisplay(), frameId(), cursor.handle());
01604     }
01605 
01606 Client::Position Client::mousePosition( const QPoint& p ) const
01607     {
01608     if( decoration != NULL )
01609         return decoration->mousePosition( p );
01610     return PositionCenter;
01611     }
01612 
01613 void Client::updateAllowedActions( bool force )
01614     {
01615     if( !isManaged() && !force )
01616         return;
01617     unsigned long old_allowed_actions = allowed_actions;
01618     allowed_actions = 0;
01619     if( isMovable())
01620         allowed_actions |= NET::ActionMove;
01621     if( isResizable())
01622         allowed_actions |= NET::ActionResize;
01623     if( isMinimizable())
01624         allowed_actions |= NET::ActionMinimize;
01625     if( isShadeable())
01626         allowed_actions |= NET::ActionShade;
01627     // sticky state not supported
01628     if( isMaximizable())
01629         allowed_actions |= NET::ActionMax;
01630     if( userCanSetFullScreen())
01631         allowed_actions |= NET::ActionFullScreen;
01632     allowed_actions |= NET::ActionChangeDesktop; // always (pagers shouldn't show Docks etc.)
01633     if( isCloseable())
01634         allowed_actions |= NET::ActionClose;
01635     if( old_allowed_actions == allowed_actions )
01636         return;
01637     // TODO this could be delayed and compressed - it's only for pagers etc. anyway
01638     info->setAllowedActions( allowed_actions );
01639     // TODO this should also tell the decoration, so that it can update the buttons
01640     }
01641 
01642 void Client::autoRaise()
01643     {
01644     workspace()->raiseClient( this );
01645     delete autoRaiseTimer;
01646     autoRaiseTimer = 0;
01647     }
01648 
01649 void Client::cancelAutoRaise()
01650     {
01651     delete autoRaiseTimer;
01652     autoRaiseTimer = 0;
01653     }
01654 
01655 #ifdef NDEBUG
01656 kndbgstream& operator<<( kndbgstream& stream, const Client* ) { return stream; }
01657 kndbgstream& operator<<( kndbgstream& stream, const ClientList&  ) { return stream; }
01658 kndbgstream& operator<<( kndbgstream& stream, const ConstClientList& ) { return stream; }
01659 #else
01660 kdbgstream& operator<<( kdbgstream& stream, const Client* cl )
01661     {
01662     if( cl == NULL )
01663         return stream << "\'NULL_CLIENT\'";
01664     return stream << "\'ID:" << cl->window() << ";WMCLASS:" << cl->resourceClass() << ":" << cl->resourceName() << ";Caption:" << cl->caption() << "\'";
01665     }
01666 kdbgstream& operator<<( kdbgstream& stream, const ClientList& list )
01667     {
01668     stream << "LIST:(";
01669     bool first = true;
01670     for( ClientList::ConstIterator it = list.begin();
01671          it != list.end();
01672          ++it )
01673         {
01674         if( !first )
01675             stream << ":";
01676         first = false;
01677         stream << *it;
01678         }
01679     stream << ")";
01680     return stream;
01681     }
01682 kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& list )
01683     {
01684     stream << "LIST:(";
01685     bool first = true;
01686     for( ConstClientList::ConstIterator it = list.begin();
01687          it != list.end();
01688          ++it )
01689         {
01690         if( !first )
01691             stream << ":";
01692         first = false;
01693         stream << *it;
01694         }
01695     stream << ")";
01696     return stream;
01697     }
01698 #endif
01699 
01700 QPixmap * kwin_get_menu_pix_hack()
01701     {
01702     static QPixmap p;
01703     if ( p.isNull() )
01704         p = SmallIcon( "bx2" );
01705     return &p;
01706     }
01707 
01708 } // namespace
01709 
01710 #include "client.moc"
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Mar 5 04:41:13 2004 by doxygen 1.3.6-20040222 written by Dimitri van Heesch, © 1997-2003