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