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