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