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 set propagate the input shape
00482           // (it's the same like the bounding shape by default)
00483         XShapeCombineShape( qt_xdisplay(), frameId(), ShapeInput, 0, 0,
00484                            frameId(), ShapeBounding, ShapeSet );
00485         XShapeCombineShape( qt_xdisplay(), frameId(), ShapeInput,
00486                            clientPos().x(), clientPos().y(),
00487                            window(), ShapeBounding, ShapeSubtract );
00488         XShapeCombineShape( qt_xdisplay(), frameId(), ShapeInput,
00489                            clientPos().x(), clientPos().y(),
00490                            window(), ShapeInput, ShapeUnion );
00491         }
00492     }
00493 
00494 void Client::setMask( const QRegion& reg, int mode )
00495     {
00496     _mask = reg;
00497     if( reg.isNull())
00498         XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00499             None, ShapeSet );
00500     else if( mode == X::Unsorted )
00501         XShapeCombineRegion( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00502             reg.handle(), ShapeSet );
00503     else
00504         {
00505         QMemArray< QRect > rects = reg.rects();
00506         XRectangle* xrects = new XRectangle[ rects.count() ];
00507         for( unsigned int i = 0;
00508              i < rects.count();
00509              ++i )
00510             {
00511             xrects[ i ].x = rects[ i ].x();
00512             xrects[ i ].y = rects[ i ].y();
00513             xrects[ i ].width = rects[ i ].width();
00514             xrects[ i ].height = rects[ i ].height();
00515             }
00516         XShapeCombineRectangles( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00517             xrects, rects.count(), ShapeSet, mode );
00518         delete[] xrects;
00519         }
00520     updateShape();
00521     }
00522 
00523 QRegion Client::mask() const
00524     {
00525     if( _mask.isEmpty())
00526         return QRegion( 0, 0, width(), height());
00527     return _mask;
00528     }
00529     
00530 void Client::setShapable(bool b)
00531     {
00532     long tmp = b?1:0;
00533     XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shapable, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &tmp, 1L);
00534     }
00535 
00536 void Client::hideClient( bool hide )
00537     {
00538     if( hidden == hide )
00539         return;
00540     hidden = hide;
00541     updateVisibility();
00542     }
00543     
00544 /*
00545   Returns whether the window is minimizable or not
00546  */
00547 bool Client::isMinimizable() const
00548     {
00549     if( isSpecialWindow())
00550         return false;
00551     if( isTransient())
00552         { // #66868 - let other xmms windows be minimized when the mainwindow is minimized
00553         bool shown_mainwindow = false;
00554         ClientList mainclients = mainClients();
00555         for( ClientList::ConstIterator it = mainclients.begin();
00556              it != mainclients.end();
00557              ++it )
00558             {
00559             if( (*it)->isShown( true ))
00560                 shown_mainwindow = true;
00561             }
00562         if( !shown_mainwindow )
00563             return true;
00564         }
00565     // this is here because kicker's taskbar doesn't provide separate entries
00566     // for windows with an explicitly given parent
00567     // TODO perhaps this should be redone
00568     if( transientFor() != NULL )
00569         return false;
00570     if( !wantsTabFocus()) // SELI - NET::Utility? why wantsTabFocus() - skiptaskbar? ?
00571         return false;
00572     return true;
00573     }
00574 
00578 void Client::minimize( bool avoid_animation )
00579     {
00580     if ( !isMinimizable() || isMinimized())
00581         return;
00582 
00583     Notify::raise( Notify::Minimize );
00584 
00585     // SELI mainClients().isEmpty() ??? - and in unminimize() too
00586     if ( mainClients().isEmpty() && isOnCurrentDesktop() && isShown( true ) && !avoid_animation )
00587         animateMinimizeOrUnminimize( true ); // was visible or shaded
00588 
00589     minimized = true;
00590 
00591     updateVisibility();
00592     updateAllowedActions();
00593     workspace()->updateMinimizedOfTransients( this );
00594     updateWindowRules();
00595     workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast );
00596     }
00597 
00598 void Client::unminimize( bool avoid_animation )
00599     {
00600     if( !isMinimized())
00601         return;
00602 
00603     Notify::raise( Notify::UnMinimize );
00604     minimized = false;
00605     if( isOnCurrentDesktop() && isShown( true ))
00606         {
00607         if( mainClients().isEmpty() && !avoid_animation )
00608             animateMinimizeOrUnminimize( FALSE );
00609         }
00610     updateVisibility();
00611     updateAllowedActions();
00612     workspace()->updateMinimizedOfTransients( this );
00613     updateWindowRules();
00614     }
00615 
00616 extern bool         blockAnimation;
00617 
00618 void Client::animateMinimizeOrUnminimize( bool minimize )
00619     {
00620     if ( blockAnimation )
00621         return;
00622     if ( !options->animateMinimize )
00623         return;
00624 
00625     if( decoration != NULL && decoration->animateMinimize( minimize ))
00626         return; // decoration did it
00627 
00628     // the function is a bit tricky since it will ensure that an
00629     // animation action needs always the same time regardless of the
00630     // performance of the machine or the X-Server.
00631 
00632     float lf,rf,tf,bf,step;
00633 
00634     int speed = options->animateMinimizeSpeed;
00635     if ( speed > 10 )
00636         speed = 10;
00637     if ( speed < 0 )
00638         speed = 0;
00639 
00640     step = 40. * (11 - speed );
00641 
00642     NETRect r = info->iconGeometry();
00643     QRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height );
00644     if ( !icongeom.isValid() )
00645         return;
00646 
00647     QPixmap pm = animationPixmap( minimize ? width() : icongeom.width() );
00648 
00649     QRect before, after;
00650     if ( minimize ) 
00651         {
00652         before = QRect( x(), y(), width(), pm.height() );
00653         after = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
00654         }
00655     else 
00656         {
00657         before = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
00658         after = QRect( x(), y(), width(), pm.height() );
00659         }
00660 
00661     lf = (after.left() - before.left())/step;
00662     rf = (after.right() - before.right())/step;
00663     tf = (after.top() - before.top())/step;
00664     bf = (after.bottom() - before.bottom())/step;
00665 
00666     grabXServer();
00667 
00668     QRect area = before;
00669     QRect area2;
00670     QPixmap pm2;
00671 
00672     QTime t;
00673     t.start();
00674     float diff;
00675 
00676     QPainter p ( workspace()->desktopWidget() );
00677     bool need_to_clear = FALSE;
00678     QPixmap pm3;
00679     do 
00680         {
00681         if (area2 != area)
00682             {
00683             pm = animationPixmap( area.width() );
00684             pm2 = QPixmap::grabWindow( qt_xrootwin(), area.x(), area.y(), area.width(), area.height() );
00685             p.drawPixmap( area.x(), area.y(), pm );
00686             if ( need_to_clear ) 
00687                 {
00688                 p.drawPixmap( area2.x(), area2.y(), pm3 );
00689                 need_to_clear = FALSE;
00690                 }
00691             area2 = area;
00692             }
00693         XFlush(qt_xdisplay());
00694         XSync( qt_xdisplay(), FALSE );
00695         diff = t.elapsed();
00696         if (diff > step)
00697             diff = step;
00698         area.setLeft(before.left() + int(diff*lf));
00699         area.setRight(before.right() + int(diff*rf));
00700         area.setTop(before.top() + int(diff*tf));
00701         area.setBottom(before.bottom() + int(diff*bf));
00702         if (area2 != area ) 
00703             {
00704             if ( area2.intersects( area ) )
00705                 p.drawPixmap( area2.x(), area2.y(), pm2 );
00706             else 
00707                 { // no overlap, we can clear later to avoid flicker
00708                 pm3 = pm2;
00709                 need_to_clear = TRUE;
00710                 }
00711             }
00712         } while ( t.elapsed() < step);
00713     if (area2 == area || need_to_clear )
00714         p.drawPixmap( area2.x(), area2.y(), pm2 );
00715 
00716     p.end();
00717     ungrabXServer();
00718     }
00719 
00720 
00724 QPixmap Client::animationPixmap( int w )
00725     {
00726     QFont font = options->font(isActive());
00727     QFontMetrics fm( font );
00728     QPixmap pm( w, fm.lineSpacing() );
00729     pm.fill( options->color(Options::ColorTitleBar, isActive() || isMinimized() ) );
00730     QPainter p( &pm );
00731     p.setPen(options->color(Options::ColorFont, isActive() || isMinimized() ));
00732     p.setFont(options->font(isActive()));
00733     p.drawText( pm.rect(), AlignLeft|AlignVCenter|SingleLine, caption() );
00734     return pm;
00735     }
00736 
00737 
00738 bool Client::isShadeable() const
00739     {
00740     return !isSpecialWindow() && !noBorder();
00741     }
00742 
00743 void Client::setShade( ShadeMode mode )
00744     {
00745     if( !isShadeable())
00746         return;
00747     mode = rules()->checkShade( mode );
00748     if( shade_mode == mode )
00749         return;
00750     bool was_shade = isShade();
00751     ShadeMode was_shade_mode = shade_mode;
00752     shade_mode = mode;
00753     if( was_shade == isShade())
00754         {
00755         if( decoration != NULL ) // decoration may want to update after e.g. hover-shade changes
00756             decoration->shadeChange();
00757         return; // no real change in shaded state
00758         }
00759 
00760     if( shade_mode == ShadeNormal )
00761         {
00762         if ( isShown( true ) && isOnCurrentDesktop())
00763                 Notify::raise( Notify::ShadeUp );
00764         }
00765     else if( shade_mode == ShadeNone )
00766         {
00767         if( isShown( true ) && isOnCurrentDesktop())
00768                 Notify::raise( Notify::ShadeDown );
00769         }
00770 
00771     assert( decoration != NULL ); // noborder windows can't be shaded
00772     GeometryUpdatesPostponer blocker( this );
00773     // decorations may turn off some borders when shaded
00774     decoration->borders( border_left, border_right, border_top, border_bottom );
00775 
00776     int as = options->animateShade? 10 : 1;
00777 // TODO all this unmapping, resizing etc. feels too much duplicated from elsewhere
00778     if ( isShade()) 
00779         { // shade_mode == ShadeNormal
00780         // we're about to shade, texx xcompmgr to prepare
00781         long _shade = 1;
00782         XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L);
00783         // shade
00784         int h = height();
00785         shade_geometry_change = true;
00786         QSize s( sizeForClientSize( QSize( clientSize())));
00787         s.setHeight( border_top + border_bottom );
00788         XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
00789         XUnmapWindow( qt_xdisplay(), wrapper );
00790         XUnmapWindow( qt_xdisplay(), client );
00791         XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00792         //as we hid the unmap event, xcompmgr didn't recognize the client wid has vanished, so we'll extra inform it        
00793         //done xcompmgr workaround
00794 // FRAME       repaint( FALSE );
00795 //        bool wasStaticContents = testWFlags( WStaticContents );
00796 //        setWFlags( WStaticContents );
00797         int step = QMAX( 4, QABS( h - s.height() ) / as )+1;
00798         do 
00799             {
00800             h -= step;
00801             XResizeWindow( qt_xdisplay(), frameId(), s.width(), h );
00802             resizeDecoration( QSize( s.width(), h ));
00803             QApplication::syncX();
00804             } while ( h > s.height() + step );
00805 //        if ( !wasStaticContents )
00806 //            clearWFlags( WStaticContents );
00807         plainResize( s );
00808         shade_geometry_change = false;
00809         if( isActive())
00810             {
00811             if( was_shade_mode == ShadeHover )
00812                 workspace()->activateNextClient( this );
00813             else
00814                 workspace()->focusToNull();
00815             }
00816         // tell xcompmgr shade's done
00817         _shade = 2;
00818         XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L);    
00819         }
00820     else 
00821         {
00822         int h = height();
00823         shade_geometry_change = true;
00824         QSize s( sizeForClientSize( clientSize()));
00825 // FRAME       bool wasStaticContents = testWFlags( WStaticContents );
00826 //        setWFlags( WStaticContents );
00827         int step = QMAX( 4, QABS( h - s.height() ) / as )+1;
00828         do 
00829             {
00830             h += step;
00831             XResizeWindow( qt_xdisplay(), frameId(), s.width(), h );
00832             resizeDecoration( QSize( s.width(), h ));
00833             // assume a border
00834             // we do not have time to wait for X to send us paint events
00835 // FRAME           repaint( 0, h - step-5, width(), step+5, TRUE);
00836             QApplication::syncX();
00837             } while ( h < s.height() - step );
00838 //        if ( !wasStaticContents )
00839 //            clearWFlags( WStaticContents );
00840         shade_geometry_change = false;
00841         plainResize( s );
00842         if( shade_mode == ShadeHover || shade_mode == ShadeActivated )
00843             setActive( TRUE );
00844         XMapWindow( qt_xdisplay(), wrapperId());
00845         XMapWindow( qt_xdisplay(), window());
00846         XDeleteProperty (qt_xdisplay(), client, atoms->net_wm_window_shade);
00847         if ( isActive() )
00848             workspace()->requestFocus( this );
00849         }
00850     checkMaximizeGeometry();
00851     info->setState( isShade() ? NET::Shaded : 0, NET::Shaded );
00852     info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden );
00853     updateVisibility();
00854     updateAllowedActions();
00855     workspace()->updateMinimizedOfTransients( this );
00856     decoration->shadeChange();
00857     updateWindowRules();
00858     }
00859 
00860 void Client::shadeHover()
00861     {
00862     setShade( ShadeHover );
00863     cancelShadeHover();
00864     }
00865 
00866 void Client::cancelShadeHover()
00867     {
00868     delete shadeHoverTimer;
00869     shadeHoverTimer = 0;
00870     }
00871 
00872 void Client::toggleShade()
00873     {
00874     // if the mode is ShadeHover or ShadeActive, cancel shade too
00875     setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone );
00876     }
00877 
00878 void Client::updateVisibility()
00879     {
00880     if( deleting )
00881         return;
00882     bool show = true;
00883     if( hidden )
00884         {
00885         setMappingState( IconicState );
00886         info->setState( NET::Hidden, NET::Hidden );
00887         setSkipTaskbar( true, false ); // also hide from taskbar
00888         rawHide();
00889         show = false;
00890         }
00891     else
00892         {
00893         setSkipTaskbar( original_skip_taskbar, false );
00894         }
00895     if( minimized )
00896         {
00897         setMappingState( IconicState );
00898         info->setState( NET::Hidden, NET::Hidden );
00899         rawHide();
00900         show = false;
00901         }
00902     if( show )
00903         info->setState( 0, NET::Hidden );
00904     if( !isOnCurrentDesktop())
00905         {
00906         setMappingState( IconicState );
00907         rawHide();
00908         show = false;
00909         }
00910     if( show )
00911         {
00912         bool belongs_to_desktop = false;
00913         for( ClientList::ConstIterator it = group()->members().begin();
00914              it != group()->members().end();
00915              ++it )
00916             if( (*it)->isDesktop())
00917                 {
00918                 belongs_to_desktop = true;
00919                 break;
00920                 }
00921         if( !belongs_to_desktop && workspace()->showingDesktop())
00922             workspace()->resetShowingDesktop( true );
00923         if( isShade())
00924             setMappingState( IconicState );
00925         else
00926             setMappingState( NormalState );
00927         rawShow();
00928         }
00929     }
00930 
00935 void Client::setMappingState(int s)
00936     {
00937     assert( client != None );
00938     assert( !deleting || s == WithdrawnState );
00939     if( mapping_state == s )
00940         return;
00941     bool was_unmanaged = ( mapping_state == WithdrawnState );
00942     mapping_state = s;
00943     if( mapping_state == WithdrawnState )
00944         {
00945         XDeleteProperty( qt_xdisplay(), window(), qt_wm_state );
00946         return;
00947         }
00948     assert( s == NormalState || s == IconicState );
00949 
00950     unsigned long data[2];
00951     data[0] = (unsigned long) s;
00952     data[1] = (unsigned long) None;
00953     XChangeProperty(qt_xdisplay(), window(), qt_wm_state, qt_wm_state, 32,
00954         PropModeReplace, (unsigned char *)data, 2);
00955 
00956     if( was_unmanaged ) // manage() did postpone_geometry_updates = 1, now it's ok to finally set the geometry
00957         postponeGeometryUpdates( false );
00958     }
00959 
00964 void Client::rawShow()
00965     {
00966     if( decoration != NULL )
00967         decoration->widget()->show(); // not really necessary, but let it know the state
00968     XMapWindow( qt_xdisplay(), frame );
00969     if( !isShade())
00970         {
00971         XMapWindow( qt_xdisplay(), wrapper );
00972         XMapWindow( qt_xdisplay(), client );
00973         }
00974     }
00975 
00981 void Client::rawHide()
00982     {
00983 // Here it may look like a race condition, as some other client might try to unmap
00984 // the window between these two XSelectInput() calls. However, they're supposed to
00985 // use XWithdrawWindow(), which also sends a synthetic event to the root window,
00986 // which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify
00987 // will be missed is also very minimal, so I don't think it's needed to grab the server
00988 // here.
00989     XSelectInput( qt_xdisplay(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
00990     XUnmapWindow( qt_xdisplay(), frame );
00991     XUnmapWindow( qt_xdisplay(), wrapper );
00992     XUnmapWindow( qt_xdisplay(), client );
00993     XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00994     if( decoration != NULL )
00995         decoration->widget()->hide(); // not really necessary, but let it know the state
00996     workspace()->clientHidden( this );
00997     }
00998 
00999 void Client::sendClientMessage(Window w, Atom a, Atom protocol, long data1, long data2, long data3)
01000     {
01001     XEvent ev;
01002     long mask;
01003 
01004     memset(&ev, 0, sizeof(ev));
01005     ev.xclient.type = ClientMessage;
01006     ev.xclient.window = w;
01007     ev.xclient.message_type = a;
01008     ev.xclient.format = 32;
01009     ev.xclient.data.l[0] = protocol;
01010     ev.xclient.data.l[1] = qt_x_time;
01011     ev.xclient.data.l[2] = data1;
01012     ev.xclient.data.l[3] = data2;
01013     ev.xclient.data.l[4] = data3;
01014     mask = 0L;
01015     if (w == qt_xrootwin())
01016       mask = SubstructureRedirectMask;        /* magic! */
01017     XSendEvent(qt_xdisplay(), w, False, mask, &ev);
01018     }
01019 
01020 /*
01021   Returns whether the window may be closed (have a close button)
01022  */
01023 bool Client::isCloseable() const
01024     {
01025     return rules()->checkCloseable( motif_may_close && !isSpecialWindow());
01026     }
01027 
01032 void Client::closeWindow()
01033     {
01034     if( !isCloseable())
01035         return;
01036     // Update user time, because the window may create a confirming dialog.
01037     updateUserTime(); 
01038     if ( Pdeletewindow )
01039         {
01040         Notify::raise( Notify::Close );
01041         sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window);
01042         pingWindow();
01043         }
01044     else 
01045         {
01046         // client will not react on wm_delete_window. We have not choice
01047         // but destroy his connection to the XServer.
01048         killWindow();
01049         }
01050     }
01051 
01052 
01056 void Client::killWindow()
01057     {
01058     kdDebug( 1212 ) << "Client::killWindow():" << caption() << endl;
01059     // not sure if we need an Notify::Kill or not.. until then, use
01060     // Notify::Close
01061     Notify::raise( Notify::Close );
01062 
01063     if( isDialog())
01064         Notify::raise( Notify::TransDelete );
01065     if( isNormalWindow())
01066         Notify::raise( Notify::Delete );
01067     killProcess( false );
01068     // always kill this client at the server
01069     XKillClient(qt_xdisplay(), window() );
01070     destroyClient();
01071     }
01072 
01073 // send a ping to the window using _NET_WM_PING if possible
01074 // if it doesn't respond within a reasonable time, it will be
01075 // killed
01076 void Client::pingWindow()
01077     {
01078     if( !Pping )
01079         return; // can't ping :(
01080     if( options->killPingTimeout == 0 )
01081         return; // turned off
01082     if( ping_timer != NULL )
01083         return; // pinging already
01084     ping_timer = new QTimer( this );
01085     connect( ping_timer, SIGNAL( timeout()), SLOT( pingTimeout()));
01086     ping_timer->start( options->killPingTimeout, true );
01087     ping_timestamp = qt_x_time;
01088     workspace()->sendPingToWindow( window(), ping_timestamp );
01089     }
01090 
01091 void Client::gotPing( Time timestamp )
01092     {
01093     if( timestamp != ping_timestamp )
01094         return;
01095     delete ping_timer;
01096     ping_timer = NULL;
01097     if( process_killer != NULL )
01098         {
01099         process_killer->kill();
01100         delete process_killer;
01101         process_killer = NULL;
01102         }
01103     }
01104 
01105 void Client::pingTimeout()
01106     {
01107     kdDebug( 1212 ) << "Ping timeout:" << caption() << endl;
01108     delete ping_timer;
01109     ping_timer = NULL;
01110     killProcess( true, ping_timestamp );
01111     }
01112 
01113 void Client::killProcess( bool ask, Time timestamp )
01114     {
01115     if( process_killer != NULL )
01116         return;
01117     Q_ASSERT( !ask || timestamp != CurrentTime );
01118     QCString machine = wmClientMachine( true );
01119     pid_t pid = info->pid();
01120     if( pid <= 0 || machine.isEmpty()) // needed properties missing
01121         return;
01122     kdDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")" << endl;
01123     if( !ask )
01124         {
01125         if( machine != "localhost" )
01126             {
01127             KProcess proc;
01128             proc << "xon" << machine << "kill" << pid;
01129             proc.start( KProcess::DontCare );
01130             }
01131         else
01132             ::kill( pid, SIGTERM );
01133         }
01134     else
01135         { // SELI TODO handle the window created by handler specially (on top,urgent?)
01136         process_killer = new KProcess( this );
01137         *process_killer << KStandardDirs::findExe( "kwin_killer_helper" )
01138             << "--pid" << QCString().setNum( pid ) << "--hostname" << machine
01139             << "--windowname" << caption().utf8()
01140             << "--applicationname" << resourceClass()
01141             << "--wid" << QCString().setNum( window())
01142             << "--timestamp" << QCString().setNum( timestamp );
01143         connect( process_killer, SIGNAL( processExited( KProcess* )),
01144             SLOT( processKillerExited()));
01145         if( !process_killer->start( KProcess::NotifyOnExit ))
01146             {
01147             delete process_killer;
01148             process_killer = NULL;
01149             return;
01150             }
01151         }
01152     }
01153 
01154 void Client::processKillerExited()
01155     {
01156     kdDebug( 1212 ) << "Killer exited" << endl;
01157     delete process_killer;
01158     process_killer = NULL;
01159     }
01160 
01161 void Client::setSkipTaskbar( bool b, bool from_outside )
01162     {
01163     int was_wants_tab_focus = wantsTabFocus();
01164     if( from_outside )
01165         {
01166         b = rules()->checkSkipTaskbar( b );
01167         original_skip_taskbar = b;
01168         }
01169     if ( b == skipTaskbar() )
01170         return;
01171     skip_taskbar = b;
01172     info->setState( b?NET::SkipTaskbar:0, NET::SkipTaskbar );
01173     updateWindowRules();
01174     if( was_wants_tab_focus != wantsTabFocus())
01175         workspace()->updateFocusChains( this,
01176             isActive() ? Workspace::FocusChainMakeFirst : Workspace::FocusChainUpdate );
01177     }
01178 
01179 void Client::setSkipPager( bool b )
01180     {
01181     b = rules()->checkSkipPager( b );
01182     if ( b == skipPager() )
01183         return;
01184     skip_pager = b;
01185     info->setState( b?NET::SkipPager:0, NET::SkipPager );
01186     updateWindowRules();
01187     }
01188 
01189 void Client::setModal( bool m )
01190     { // Qt-3.2 can have even modal normal windows :(
01191     if( modal == m )
01192         return;
01193     modal = m;
01194     if( !modal )
01195         return;
01196     // changing modality for a mapped window is weird (?)
01197     // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG
01198     }
01199 
01200 void Client::setDesktop( int desktop )
01201     {
01202     if( desktop != NET::OnAllDesktops ) // do range check
01203         desktop = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desktop ));
01204     desktop = rules()->checkDesktop( desktop );
01205     if( desk == desktop )
01206         return;
01207     int was_desk = desk;
01208     desk = desktop;
01209     info->setDesktop( desktop );
01210     if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops ))
01211         { // onAllDesktops changed
01212         if ( isShown( true ))
01213             Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops );
01214         workspace()->updateOnAllDesktopsOfTransients( this );
01215         }
01216     if( decoration != NULL )
01217         decoration->desktopChange();
01218     workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst );
01219     updateVisibility();
01220     updateWindowRules();
01221     }
01222 
01223 void Client::setOnAllDesktops( bool b )
01224     {
01225     if(( b && isOnAllDesktops())
01226         || ( !b && !isOnAllDesktops()))
01227         return;
01228     if( b )
01229         setDesktop( NET::OnAllDesktops );
01230     else
01231         setDesktop( workspace()->currentDesktop());
01232     }
01233 
01234 bool Client::isOnCurrentDesktop() const
01235     {
01236     return isOnDesktop( workspace()->currentDesktop());
01237     }
01238 
01239 int Client::screen() const
01240     {
01241     if( !options->xineramaEnabled )
01242         return 0;
01243     return workspace()->screenNumber( geometry().center());
01244     }
01245 
01246 bool Client::isOnScreen( int screen ) const
01247     {
01248     if( !options->xineramaEnabled )
01249         return screen == 0;
01250     return workspace()->screenGeometry( screen ).intersects( geometry());
01251     }
01252 
01253 // performs activation and/or raising of the window
01254 void Client::takeActivity( int flags, bool handled, allowed_t )
01255     {
01256     if( !handled || !Ptakeactivity )
01257         {
01258         if( flags & ActivityFocus )
01259             takeFocus( Allowed );
01260         if( flags & ActivityRaise )
01261             workspace()->raiseClient( this );
01262         return;
01263         }
01264 
01265 #ifndef NDEBUG
01266     static Time previous_activity_timestamp;
01267     static Client* previous_client;
01268     if( previous_activity_timestamp == qt_x_time && previous_client != this )
01269         {
01270         kdDebug( 1212 ) << "Repeated use of the same X timestamp for activity" << endl;
01271         kdDebug( 1212 ) << kdBacktrace() << endl;
01272         }
01273     previous_activity_timestamp = qt_x_time;
01274     previous_client = this;
01275 #endif
01276     workspace()->sendTakeActivity( this, qt_x_time, flags );
01277     }
01278 
01279 // performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS
01280 void Client::takeFocus( allowed_t )
01281     {
01282 #ifndef NDEBUG
01283     static Time previous_focus_timestamp;
01284     static Client* previous_client;
01285     if( previous_focus_timestamp == qt_x_time && previous_client != this )
01286         {
01287         kdDebug( 1212 ) << "Repeated use of the same X timestamp for focus" << endl;
01288         kdDebug( 1212 ) << kdBacktrace() << endl;
01289         }
01290     previous_focus_timestamp = qt_x_time;
01291     previous_client = this;
01292 #endif
01293     if ( rules()->checkAcceptFocus( input ))
01294         {
01295         XSetInputFocus( qt_xdisplay(), window(), RevertToPointerRoot, qt_x_time );
01296         }
01297     if ( Ptakefocus )
01298         sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus);
01299     workspace()->setShouldGetFocus( this );
01300     }
01301 
01309 bool Client::providesContextHelp() const
01310     {
01311     return Pcontexthelp;
01312     }
01313 
01314 
01321 void Client::showContextHelp()
01322     {
01323     if ( Pcontexthelp ) 
01324         {
01325         sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help);
01326         QWhatsThis::enterWhatsThisMode(); // SELI?
01327         }
01328     }
01329 
01330 
01335 void Client::fetchName()
01336     {
01337     setCaption( readName());
01338     }
01339 
01340 QString Client::readName() const
01341     {
01342     if ( info->name() && info->name()[ 0 ] != '\0' ) 
01343         return QString::fromUtf8( info->name() );
01344     else 
01345         return KWin::readNameProperty( window(), XA_WM_NAME );
01346     }
01347     
01348 KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption());
01349 
01350 void Client::setCaption( const QString& s, bool force )
01351     {
01352     if ( s != cap_normal || force ) 
01353         {
01354         bool reset_name = force;
01355         for( unsigned int i = 0;
01356              i < s.length();
01357              ++i )
01358             if( !s[ i ].isPrint())
01359                 s[ i ] = ' ';
01360         cap_normal = s;
01361         bool was_suffix = ( !cap_suffix.isEmpty());
01362         QString machine_suffix;
01363         if( wmClientMachine( false ) != "localhost" && !isLocalMachine( wmClientMachine( false )))
01364             machine_suffix = " <@" + wmClientMachine( true ) + ">";
01365         QString shortcut_suffix = !shortcut().isNull() ? ( " {" + shortcut().toString() + "}" ) : "";
01366         cap_suffix = machine_suffix + shortcut_suffix;
01367         if ( ( !isSpecialWindow() || isToolbar()) && workspace()->findClient( FetchNameInternalPredicate( this ))) 
01368             {
01369             int i = 2;
01370             do 
01371                 {
01372                 cap_suffix = machine_suffix + " <" + QString::number(i) + ">" + shortcut_suffix;
01373                 i++;
01374                 } while ( workspace()->findClient( FetchNameInternalPredicate( this )));
01375             info->setVisibleName( caption().utf8() );
01376             reset_name = false;
01377             }
01378         if(( was_suffix && cap_suffix.isEmpty()
01379             || reset_name )) // if it was new window, it may have old value still set, if the window is reused
01380             {
01381             info->setVisibleName( "" ); // remove
01382             info->setVisibleIconName( "" ); // remove
01383             }
01384         else if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set
01385             info->setVisibleIconName( ( cap_iconic + cap_suffix ).utf8() );
01386 
01387         if( isManaged() && decoration != NULL )
01388                 decoration->captionChange();
01389         }
01390     }
01391 
01392 void Client::updateCaption()
01393     {
01394     setCaption( cap_normal, true );
01395     }
01396 
01397 void Client::fetchIconicName()
01398     {
01399     QString s;
01400     if ( info->iconName() && info->iconName()[ 0 ] != '\0' ) 
01401         s = QString::fromUtf8( info->iconName() );
01402     else 
01403         s = KWin::readNameProperty( window(), XA_WM_ICON_NAME );
01404     if ( s != cap_iconic ) 
01405         {
01406     bool was_set = !cap_iconic.isEmpty();
01407         cap_iconic = s;
01408         if( !cap_suffix.isEmpty())
01409         {
01410         if( !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set
01411             info->setVisibleIconName( ( s + cap_suffix ).utf8() );
01412         else if( was_set )
01413         info->setVisibleIconName( "" ); //remove
01414         }
01415         }
01416     }
01417 
01420 QString Client::caption( bool full ) const
01421     {
01422     return full ? cap_normal + cap_suffix : cap_normal;
01423     }
01424 
01425 void Client::getWMHints()
01426     {
01427     XWMHints *hints = XGetWMHints(qt_xdisplay(), window() );
01428     input = true;
01429     window_group = None;
01430     urgency = false;
01431     if ( hints )
01432         {
01433         if( hints->flags & InputHint )
01434             input = hints->input;
01435         if( hints->flags & WindowGroupHint )
01436             window_group = hints->window_group;
01437         urgency = ( hints->flags & UrgencyHint ) ? true : false; // true/false needed, it's uint bitfield
01438         XFree( (char*)hints );
01439         }
01440     checkGroup();
01441     updateUrgency();
01442     updateAllowedActions(); // group affects isMinimizable()
01443     }
01444 
01445 void Client::getMotifHints()
01446     {
01447     bool mnoborder, mresize, mmove, mminimize, mmaximize, mclose;
01448     Motif::readFlags( client, mnoborder, mresize, mmove, mminimize, mmaximize, mclose );
01449     motif_noborder = mnoborder;
01450     if( !hasNETSupport()) // NETWM apps should set type and size constraints
01451         {
01452         motif_may_resize = mresize; // this should be set using minsize==maxsize, but oh well
01453         motif_may_move = mmove;
01454         }
01455     else
01456         motif_may_resize = motif_may_move = true;
01457     // mminimize; - ignore, bogus - e.g. shading or sending to another desktop is "minimizing" too
01458     // mmaximize; - ignore, bogus - maximizing is basically just resizing
01459     motif_may_close = mclose; // motif apps like to crash when they set this hint and WM closes them anyway
01460     if( isManaged())
01461         updateDecoration( true ); // check if noborder state has changed
01462     }
01463 
01464 void Client::readIcons( Window win, QPixmap* icon, QPixmap* miniicon )
01465     {    
01466     // get the icons, allow scaling
01467     if( icon != NULL )
01468         *icon = KWin::icon( win, 32, 32, TRUE, KWin::NETWM | KWin::WMHints );
01469     if( miniicon != NULL )
01470         if( icon == NULL || !icon->isNull())
01471             *miniicon = KWin::icon( win, 16, 16, TRUE, KWin::NETWM | KWin::WMHints );
01472         else
01473             *miniicon = QPixmap();
01474     }
01475 
01476 void Client::getIcons()
01477     {
01478     // first read icons from the window itself
01479     readIcons( window(), &icon_pix, &miniicon_pix );
01480     if( icon_pix.isNull())
01481         { // then try window group
01482         icon_pix = group()->icon();
01483         miniicon_pix = group()->miniIcon();
01484         }
01485     if( icon_pix.isNull() && isTransient())
01486         { // then mainclients
01487         ClientList mainclients = mainClients();
01488         for( ClientList::ConstIterator it = mainclients.begin();
01489              it != mainclients.end() && icon_pix.isNull();
01490              ++it )
01491             {
01492             icon_pix = (*it)->icon();
01493             miniicon_pix = (*it)->miniIcon();
01494             }
01495         }
01496     if( icon_pix.isNull())
01497         { // and if nothing else, load icon from classhint or xapp icon
01498         icon_pix = KWin::icon( window(), 32, 32, TRUE, KWin::ClassHint | KWin::XApp );
01499         miniicon_pix = KWin::icon( window(), 16, 16, TRUE, KWin::ClassHint | KWin::XApp );
01500         }
01501     if( isManaged() && decoration != NULL )
01502         decoration->iconChange();
01503     }
01504 
01505 void Client::getWindowProtocols()
01506     {
01507     Atom *p;
01508     int i,n;
01509 
01510     Pdeletewindow = 0;
01511     Ptakefocus = 0;
01512     Ptakeactivity = 0;
01513     Pcontexthelp = 0;
01514     Pping = 0;
01515 
01516     if (XGetWMProtocols(qt_xdisplay(), window(), &p, &n))
01517         {
01518         for (i = 0; i < n; i++)
01519             if (p[i] == atoms->wm_delete_window)
01520                 Pdeletewindow = 1;
01521             else if (p[i] == atoms->wm_take_focus)
01522                 Ptakefocus = 1;
01523             else if (p[i] == atoms->net_wm_take_activity)
01524                 Ptakeactivity = 1;
01525             else if (p[i] == atoms->net_wm_context_help)
01526                 Pcontexthelp = 1;
01527             else if (p[i] == atoms->net_wm_ping)
01528                 Pping = 1;
01529         if (n>0)
01530             XFree(p);
01531         }
01532     }
01533 
01534 static int nullErrorHandler(Display *, XErrorEvent *)
01535     {
01536     return 0;
01537     }
01538 
01542 QCString Client::staticWindowRole(WId w)
01543     {
01544     return getStringProperty(w, qt_window_role).lower();
01545     }
01546 
01550 QCString Client::staticSessionId(WId w)
01551     {
01552     return getStringProperty(w, qt_sm_client_id);
01553     }
01554 
01558 QCString Client::staticWmCommand(WId w)
01559     {
01560     return getStringProperty(w, XA_WM_COMMAND, ' ');
01561     }
01562 
01566 Window Client::staticWmClientLeader(WId w)
01567     {
01568     Atom type;
01569     int format, status;
01570     unsigned long nitems = 0;
01571     unsigned long extra = 0;
01572     unsigned char *data = 0;
01573     Window result = w;
01574     XErrorHandler oldHandler = XSetErrorHandler(nullErrorHandler);
01575     status = XGetWindowProperty( qt_xdisplay(), w, atoms->wm_client_leader, 0, 10000,
01576                                  FALSE, XA_WINDOW, &type, &format,
01577                                  &nitems, &extra, &data );
01578     XSetErrorHandler(oldHandler);
01579     if (status  == Success ) 
01580         {
01581         if (data && nitems > 0)
01582             result = *((Window*) data);
01583         XFree(data);
01584         }
01585     return result;
01586     }
01587 
01588 
01589 void Client::getWmClientLeader()
01590     {
01591     wmClientLeaderWin = staticWmClientLeader(window());
01592     }
01593 
01598 QCString Client::sessionId()
01599     {
01600     QCString result = staticSessionId(window());
01601     if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01602         result = staticSessionId(wmClientLeaderWin);
01603     return result;
01604     }
01605 
01610 QCString Client::wmCommand()
01611     {
01612     QCString result = staticWmCommand(window());
01613     if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01614         result = staticWmCommand(wmClientLeaderWin);
01615     return result;
01616     }
01617 
01618 void Client::getWmClientMachine()
01619     {
01620     client_machine = getStringProperty(window(), XA_WM_CLIENT_MACHINE);
01621     if( client_machine.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01622         client_machine = getStringProperty(wmClientLeaderWin, XA_WM_CLIENT_MACHINE);
01623     if( client_machine.isEmpty())
01624         client_machine = "localhost";
01625     }
01626 
01631 QCString Client::wmClientMachine( bool use_localhost ) const
01632     {
01633     QCString result = client_machine;
01634     if( use_localhost )
01635         { // special name for the local machine (localhost)
01636         if( result != "localhost" && isLocalMachine( result ))
01637             result = "localhost";
01638         }
01639     return result;
01640     }
01641 
01646 Window Client::wmClientLeader() const
01647     {
01648     if (wmClientLeaderWin)
01649         return wmClientLeaderWin;
01650     return window();
01651     }
01652 
01653 bool Client::wantsTabFocus() const
01654     {
01655     return ( isNormalWindow() || isDialog()) && wantsInput() && !skip_taskbar;
01656     }
01657 
01658 
01659 bool Client::wantsInput() const
01660     {
01661     return rules()->checkAcceptFocus( input || Ptakefocus );
01662     }
01663 
01664 bool Client::isDesktop() const
01665     {
01666     return windowType() == NET::Desktop;
01667     }
01668 
01669 bool Client::isDock() const
01670     {
01671     return windowType() == NET::Dock;
01672     }
01673 
01674 bool Client::isTopMenu() const
01675     {
01676     return windowType() == NET::TopMenu;
01677     }
01678 
01679 
01680 bool Client::isMenu() const
01681     {
01682     return windowType() == NET::Menu && !isTopMenu(); // because of backwards comp.
01683     }
01684 
01685 bool Client::isToolbar() const
01686     {
01687     return windowType() == NET::Toolbar;
01688     }
01689 
01690 bool Client::isSplash() const
01691     {
01692     return windowType() == NET::Splash;
01693     }
01694 
01695 bool Client::isUtility() const
01696     {
01697     return windowType() == NET::Utility;
01698     }
01699 
01700 bool Client::isDialog() const
01701     {
01702     return windowType() == NET::Dialog;
01703     }
01704 
01705 bool Client::isNormalWindow() const
01706     {
01707     return windowType() == NET::Normal;
01708     }
01709 
01710 bool Client::isSpecialWindow() const
01711     {
01712     return isDesktop() || isDock() || isSplash() || isTopMenu()
01713         || isToolbar(); // TODO
01714     }
01715 
01716 NET::WindowType Client::windowType( bool direct, int supported_types ) const
01717     {
01718     NET::WindowType wt = info->windowType( supported_types );
01719     if( direct )
01720         return wt;
01721     NET::WindowType wt2 = rules()->checkType( wt );
01722     if( wt != wt2 )
01723         {
01724         wt = wt2;
01725         info->setWindowType( wt ); // force hint change
01726         }
01727     // hacks here
01728     if( wt == NET::Menu )
01729         {
01730         // ugly hack to support the times when NET::Menu meant NET::TopMenu
01731         // if it's as wide as the screen, not very high and has its upper-left
01732         // corner a bit above the screen's upper-left cornet, it's a topmenu
01733         if( x() == 0 && y() < 0 && y() > -10 && height() < 100
01734             && abs( width() - workspace()->clientArea( FullArea, this ).width()) < 10 )
01735             wt = NET::TopMenu;
01736         }
01737     // TODO change this to rule
01738     const char* const oo_prefix = "openoffice.org"; // QCString has no startsWith()
01739     // oo_prefix is lowercase, because resourceClass() is forced to be lowercase
01740     if( qstrncmp( resourceClass(), oo_prefix, strlen( oo_prefix )) == 0 && wt == NET::Dialog )
01741         wt = NET::Normal; // see bug #66065
01742     if( wt == NET::Unknown ) // this is more or less suggested in NETWM spec
01743         wt = isTransient() ? NET::Dialog : NET::Normal;
01744     return wt;
01745     }
01746 
01751 void Client::setCursor( Position m )
01752     {
01753     if( !isResizable() || isShade())
01754         {
01755         m = PositionCenter;
01756         }
01757     switch ( m ) 
01758         {
01759         case PositionTopLeft:
01760         case PositionBottomRight:
01761             setCursor( sizeFDiagCursor );
01762             break;
01763         case PositionBottomLeft:
01764         case PositionTopRight:
01765             setCursor( sizeBDiagCursor );
01766             break;
01767         case PositionTop:
01768         case PositionBottom:
01769             setCursor( sizeVerCursor );
01770             break;
01771         case PositionLeft:
01772         case PositionRight:
01773             setCursor( sizeHorCursor );
01774             break;
01775         default:
01776             if( buttonDown && isMovable())
01777                 setCursor( sizeAllCursor );
01778             else
01779                 setCursor( arrowCursor );
01780             break;
01781         }
01782     }
01783 
01784 // TODO mit nejake checkCursor(), ktere se zavola v manage() a pri vecech, kdy by se kurzor mohl zmenit?
01785 void Client::setCursor( const QCursor& c )
01786     {
01787     if( c.handle() == cursor.handle())
01788         return;
01789     cursor = c;
01790     if( decoration != NULL )
01791         decoration->widget()->setCursor( cursor );
01792     XDefineCursor( qt_xdisplay(), frameId(), cursor.handle());
01793     }
01794 
01795 Client::Position Client::mousePosition( const QPoint& p ) const
01796     {
01797     if( decoration != NULL )
01798         return decoration->mousePosition( p );
01799     return PositionCenter;
01800     }
01801 
01802 void Client::updateAllowedActions( bool force )
01803     {
01804     if( !isManaged() && !force )
01805         return;
01806     unsigned long old_allowed_actions = allowed_actions;
01807     allowed_actions = 0;
01808     if( isMovable())
01809         allowed_actions |= NET::ActionMove;
01810     if( isResizable())
01811         allowed_actions |= NET::ActionResize;
01812     if( isMinimizable())
01813         allowed_actions |= NET::ActionMinimize;
01814     if( isShadeable())
01815         allowed_actions |= NET::ActionShade;
01816     // sticky state not supported
01817     if( isMaximizable())
01818         allowed_actions |= NET::ActionMax;
01819     if( userCanSetFullScreen())
01820         allowed_actions |= NET::ActionFullScreen;
01821     allowed_actions |= NET::ActionChangeDesktop; // always (pagers shouldn't show Docks etc.)
01822     if( isCloseable())
01823         allowed_actions |= NET::ActionClose;
01824     if( old_allowed_actions == allowed_actions )
01825         return;
01826     // TODO this could be delayed and compressed - it's only for pagers etc. anyway
01827     info->setAllowedActions( allowed_actions );
01828     // TODO this should also tell the decoration, so that it can update the buttons
01829     }
01830 
01831 void Client::autoRaise()
01832     {
01833     workspace()->raiseClient( this );
01834     cancelAutoRaise();
01835     }
01836     
01837 void Client::cancelAutoRaise()
01838     {
01839     delete autoRaiseTimer;
01840     autoRaiseTimer = 0;
01841     }
01842 
01843 void Client::setOpacity(bool translucent, uint opacity)
01844     {
01845     if (isDesktop())
01846         return; // xcompmgr does not like non solid desktops and the user could set it accidently by mouse scrolling
01847 //     qWarning("setting opacity for %d",qt_xdisplay());
01848     //rule out activated translulcency with 100% opacity
01849     if (!translucent || opacity ==  0xFFFFFFFF)
01850         {
01851         opacity_ = 0xFFFFFFFF;
01852         XDeleteProperty (qt_xdisplay(), frameId(), atoms->net_wm_window_opacity);
01853         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
01854         }
01855     else{
01856         if(opacity == opacity_)
01857             return;
01858         opacity_ = opacity;
01859         long data = opacity; // 32bit XChangeProperty needs long
01860         XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_opacity, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L);
01861         XChangeProperty(qt_xdisplay(), window(), atoms->net_wm_window_opacity, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L);
01862         }
01863     }
01864     
01865 void Client::setShadowSize(uint shadowSize)
01866     {
01867     // ignoring all individual settings - if we control a window, we control it's shadow
01868     // TODO somehow handle individual settings for docks (besides custom sizes)
01869     long data = shadowSize;
01870     XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_shadow, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L);
01871     }
01872         
01873 void Client::updateOpacity()
01874 // extra syncscreen flag allows to avoid double syncs when active state changes (as it will usually change for two windows)
01875     {
01876     if (!(isNormalWindow() || isDialog() || isUtility() )|| custom_opacity)
01877         return;
01878     if (isActive())
01879         {
01880         if( ruleOpacityActive() )
01881             setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active);
01882         else
01883             setOpacity(options->translucentActiveWindows, options->activeWindowOpacity);
01884         if (isBMP())
01885         // beep-media-player, only undecorated windows (gtk2 xmms, xmms doesn't work with compmgr at all - s.e.p. :P )
01886             {
01887             ClientList tmpGroupMembers = group()->members();
01888             ClientList activeGroupMembers;
01889             activeGroupMembers.append(this);
01890             tmpGroupMembers.remove(this);
01891             ClientList::Iterator it = tmpGroupMembers.begin();
01892             while (it != tmpGroupMembers.end())
01893             // search for next attached and not activated client and repeat if found
01894                 {
01895                 if ((*it) != this && (*it)->isBMP())
01896                 // potential "to activate" client found
01897                     {
01898 //                     qWarning("client found");
01899                     if ((*it)->touches(this)) // first test, if the new client touches the just activated one
01900                         {
01901 //                         qWarning("found client touches me");
01902                         if( ruleOpacityActive() )
01903                             (*it)->setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active);
01904                         else
01905                             (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity);
01906 //                         qWarning("activated, search restarted (1)");
01907                         (*it)->setShadowSize(options->activeWindowShadowSize);
01908                         activeGroupMembers.append(*it);
01909                         tmpGroupMembers.remove(it);
01910                         it = tmpGroupMembers.begin(); // restart, search next client
01911                         continue;
01912                         }
01913                     else
01914                         { // pot. client does not touch c, so we have to search if it touches some other activated client
01915                         bool found = false;
01916                         for( ClientList::ConstIterator it2 = activeGroupMembers.begin(); it2 != activeGroupMembers.end(); it2++ )
01917                             {
01918                             if ((*it2) != this && (*it2) != (*it) && (*it)->touches(*it2))
01919                                 {
01920 //                                 qWarning("found client touches other active client");
01921                                 if( ruleOpacityActive() )
01922                                     (*it)->setOpacity(rule_opacity_active < 0xFFFFFFFF, rule_opacity_active);
01923                                 else
01924                                     (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity);
01925                                 (*it)->setShadowSize(options->activeWindowShadowSize);
01926                                 activeGroupMembers.append(*it);
01927                                 tmpGroupMembers.remove(it);
01928                                 it = tmpGroupMembers.begin(); // reset potential client search
01929                                 found = true;
01930 //                                 qWarning("activated, search restarted (2)");
01931                                 break; // skip this loop
01932                                 }
01933                             }
01934                         if (found) continue;
01935                         }
01936                     }
01937                     it++;
01938                 }
01939             }
01940         else if (isNormalWindow())
01941         // activate dependend minor windows as well
01942             {
01943             for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ )
01944                 if ((*it)->isDialog() || (*it)->isUtility())
01945                     if( (*it)->ruleOpacityActive() )
01946                         (*it)->setOpacity((*it)->ruleOpacityActive() < 0xFFFFFFFF, (*it)->ruleOpacityActive());
01947                     else
01948                         (*it)->setOpacity(options->translucentActiveWindows, options->activeWindowOpacity);
01949             }
01950         }
01951     else
01952         {
01953         if( ruleOpacityInactive() )
01954             setOpacity(rule_opacity_inactive < 0xFFFFFFFF, rule_opacity_inactive);
01955         else
01956             setOpacity(options->translucentInactiveWindows && !(keepAbove() && options->keepAboveAsActive),
01957                     options->inactiveWindowOpacity);
01958         // deactivate dependend minor windows as well
01959         if (isBMP())
01960         // beep-media-player, only undecorated windows (gtk2 xmms, xmms doesn't work with compmgr at all - s.e.p. :P )
01961             {
01962             ClientList tmpGroupMembers = group()->members();
01963             ClientList inactiveGroupMembers;
01964             inactiveGroupMembers.append(this);
01965             tmpGroupMembers.remove(this);
01966             ClientList::Iterator it = tmpGroupMembers.begin();
01967             while ( it != tmpGroupMembers.end() )
01968             // search for next attached and not activated client and repeat if found
01969                 {
01970                 if ((*it) != this && (*it)->isBMP())
01971                 // potential "to activate" client found
01972                     {
01973 //                     qWarning("client found");
01974                     if ((*it)->touches(this)) // first test, if the new client touches the just activated one
01975                         {
01976 //                         qWarning("found client touches me");
01977                         if( (*it)->ruleOpacityInactive() )
01978                             (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive());
01979                         else
01980                             (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity);
01981                         (*it)->setShadowSize(options->inactiveWindowShadowSize);
01982 //                         qWarning("deactivated, search restarted (1)");
01983                         inactiveGroupMembers.append(*it);
01984                         tmpGroupMembers.remove(it);
01985                         it = tmpGroupMembers.begin(); // restart, search next client
01986                         continue;
01987                         }
01988                     else // pot. client does not touch c, so we have to search if it touches some other activated client
01989                         {
01990                         bool found = false;
01991                         for( ClientList::ConstIterator it2 = inactiveGroupMembers.begin(); it2 != inactiveGroupMembers.end(); it2++ )
01992                             {
01993                             if ((*it2) != this && (*it2) != (*it) && (*it)->touches(*it2))
01994                                 {
01995 //                                 qWarning("found client touches other inactive client");
01996                                 if( (*it)->ruleOpacityInactive() )
01997                                     (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive());
01998                                 else
01999                                     (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity);
02000                                 (*it)->setShadowSize(options->inactiveWindowShadowSize);
02001 //                                 qWarning("deactivated, search restarted (2)");
02002                                 inactiveGroupMembers.append(*it);
02003                                 tmpGroupMembers.remove(it);
02004                                 it = tmpGroupMembers.begin(); // reset potential client search
02005                                 found = true;
02006                                 break; // skip this loop
02007                                 }
02008                             }
02009                             if (found) continue;
02010                         }
02011                     }
02012                     it++;
02013                 }
02014             }
02015         else if (isNormalWindow())
02016             {
02017             for( ClientList::ConstIterator it = group()->members().begin(); it != group()->members().end(); it++ )
02018                 if ((*it)->isUtility()) //don't deactivate dialogs...
02019                     if( (*it)->ruleOpacityInactive() )
02020                         (*it)->setOpacity((*it)->ruleOpacityInactive() < 0xFFFFFFFF, (*it)->ruleOpacityInactive());
02021                     else
02022                         (*it)->setOpacity(options->translucentInactiveWindows && !((*it)->keepAbove() && options->keepAboveAsActive), options->inactiveWindowOpacity);
02023             }
02024         }
02025     }
02026     
02027 void Client::updateShadowSize()
02028 // extra syncscreen flag allows to avoid double syncs when active state changes (as it will usually change for two windows)
02029     {
02030     if (!(isNormalWindow() || isDialog() || isUtility() ))
02031         return;
02032     if (isActive())
02033         setShadowSize(options->activeWindowShadowSize);
02034     else
02035         setShadowSize(options->inactiveWindowShadowSize);
02036     }
02037 
02038 uint Client::ruleOpacityInactive()
02039     {
02040     return rule_opacity_inactive;// != 0 ;
02041     }
02042 
02043 uint Client::ruleOpacityActive()
02044     {
02045     return rule_opacity_active;// != 0;
02046     }
02047     
02048 bool Client::getWindowOpacity() //query translucency settings from X, returns true if window opacity is set
02049     {
02050     unsigned char *data = 0;
02051     Atom actual;
02052     int format, result;
02053     unsigned long n, left;
02054     result = XGetWindowProperty(qt_xdisplay(), window(), atoms->net_wm_window_opacity, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, /*(unsigned char **)*/ &data);
02055     if (result == Success && data != None && format == 32 )
02056         {
02057         opacity_ = *reinterpret_cast< long* >( data );
02058         custom_opacity = true;
02059 //         setOpacity(opacity_ < 0xFFFFFFFF, opacity_);
02060         XFree ((char*)data);
02061         return TRUE;
02062         }
02063     return FALSE;
02064     }
02065     
02066 void Client::setCustomOpacityFlag(bool custom)
02067     {
02068     custom_opacity = custom;
02069     }
02070     
02071 uint Client::opacity()
02072     {
02073     return opacity_;
02074     }
02075 
02076 int Client::opacityPercentage()
02077     {
02078     return int(100*((double)opacity_/0xffffffff));
02079     }
02080     
02081 bool Client::touches(const Client* c)
02082 // checks if this client borders c, needed to test beep media player window state
02083     {
02084     if (y() == c->y() + c->height()) // this bottom to c
02085         return TRUE;
02086     if (y() + height() == c->y()) // this top to c
02087         return TRUE;
02088     if (x() == c->x() + c->width()) // this right to c
02089         return TRUE;
02090     if (x() + width() == c->x()) // this left to c
02091         return TRUE;
02092     return FALSE;
02093     }
02094     
02095 void Client::setDecoHashProperty(uint topHeight, uint rightWidth, uint bottomHeight, uint leftWidth)
02096 {
02097    long data = (topHeight < 255 ? topHeight : 255) << 24 |
02098                (rightWidth < 255 ? rightWidth : 255) << 16 |
02099                (bottomHeight < 255 ? bottomHeight : 255) << 8 |
02100                (leftWidth < 255 ? leftWidth : 255);
02101     XChangeProperty(qt_xdisplay(), frameId(), atoms->net_wm_window_decohash, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &data, 1L);
02102 }
02103 
02104 void Client::unsetDecoHashProperty()
02105 {
02106    XDeleteProperty( qt_xdisplay(), frameId(), atoms->net_wm_window_decohash);
02107 }
02108     
02109 #ifndef NDEBUG
02110 kdbgstream& operator<<( kdbgstream& stream, const Client* cl )
02111     {
02112     if( cl == NULL )
02113         return stream << "\'NULL_CLIENT\'";
02114     return stream << "\'ID:" << cl->window() << ";WMCLASS:" << cl->resourceClass() << ":" << cl->resourceName() << ";Caption:" << cl->caption() << "\'";
02115     }
02116 kdbgstream& operator<<( kdbgstream& stream, const ClientList& list )
02117     {
02118     stream << "LIST:(";
02119     bool first = true;
02120     for( ClientList::ConstIterator it = list.begin();
02121          it != list.end();
02122          ++it )
02123         {
02124         if( !first )
02125             stream << ":";
02126         first = false;
02127         stream << *it;
02128         }
02129     stream << ")";
02130     return stream;
02131     }
02132 kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& list )
02133     {
02134     stream << "LIST:(";
02135     bool first = true;
02136     for( ConstClientList::ConstIterator it = list.begin();
02137          it != list.end();
02138          ++it )
02139         {
02140         if( !first )
02141             stream << ":";
02142         first = false;
02143         stream << *it;
02144         }
02145     stream << ")";
02146     return stream;
02147     }
02148 #endif
02149 
02150 QPixmap * kwin_get_menu_pix_hack()
02151     {
02152     static QPixmap p;
02153     if ( p.isNull() )
02154         p = SmallIcon( "bx2" );
02155     return &p;
02156     }
02157 
02158 } // namespace
02159 
02160 #include "client.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys