client.cpp

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