kwin Library API Documentation

client.cpp

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