• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KWin

client.cpp

Go to the documentation of this file.
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 This program is free software; you can redistribute it and/or modify
00009 it under the terms of the GNU General Public License as published by
00010 the Free Software Foundation; either version 2 of the License, or
00011 (at your option) any later version.
00012 
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 GNU General Public License for more details.
00017 
00018 You should have received a copy of the GNU General Public License
00019 along with this program.  If not, see <http://www.gnu.org/licenses/>.
00020 *********************************************************************/
00021 
00022 #include "client.h"
00023 
00024 #include <QApplication>
00025 #include <QPainter>
00026 #include <QDateTime>
00027 #include <QProcess>
00028 #include <unistd.h>
00029 #include <kstandarddirs.h>
00030 #include <QWhatsThis>
00031 #include <kwindowsystem.h>
00032 #include <kiconloader.h>
00033 #include <stdlib.h>
00034 #include <signal.h>
00035 
00036 #include "bridge.h"
00037 #include "group.h"
00038 #include "workspace.h"
00039 #include "atoms.h"
00040 #include "notifications.h"
00041 #include "rules.h"
00042 #include "scene.h"
00043 #include "effects.h"
00044 #include "deleted.h"
00045 
00046 #include <X11/extensions/shape.h>
00047 #include <QX11Info>
00048 
00049 #ifdef HAVE_XSYNC
00050 #include <X11/extensions/sync.h>
00051 #endif
00052 
00053 // put all externs before the namespace statement to allow the linker
00054 // to resolve them properly
00055 
00056 namespace KWin
00057 {
00058 
00059 /*
00060 
00061  Creating a client:
00062      - only by calling Workspace::createClient()
00063          - it creates a new client and calls manage() for it
00064 
00065  Destroying a client:
00066      - destroyClient() - only when the window itself has been destroyed
00067      - releaseWindow() - the window is kept, only the client itself is destroyed
00068 
00069 */
00070 
00071 
00083 Client::Client( Workspace *ws )
00084     :   Toplevel( ws ),
00085         client( None ),
00086         wrapper( None ),
00087         decoration( NULL ),
00088         bridge( new Bridge( this )),
00089         move_faked_activity( false ),
00090         move_resize_grab_window( None ),
00091         move_resize_has_keyboard_grab( false ),
00092         transient_for( NULL ),
00093         transient_for_id( None ),
00094         original_transient_for_id( None ),
00095         autoRaiseTimer( NULL ),
00096         shadeHoverTimer( NULL ),
00097         delayedMoveResizeTimer( NULL ),
00098         in_group( NULL ),
00099         window_group( None ),
00100         in_layer( UnknownLayer ),
00101         ping_timer( NULL ),
00102         process_killer( NULL ),
00103         user_time( CurrentTime ), // not known yet
00104         allowed_actions( 0 ),
00105         block_geometry_updates( 0 ),
00106         pending_geometry_update( PendingGeometryNone ),
00107         shade_geometry_change( false ),
00108 #ifdef HAVE_XSYNC
00109         sync_counter( None ),
00110         sync_alarm( None ),
00111 #endif
00112         sync_timeout( NULL ),
00113         sync_resize_pending( false ),
00114         border_left( 0 ),
00115         border_right( 0 ),
00116         border_top( 0 ),
00117         border_bottom( 0 ),
00118         sm_stacking_order( -1 ),
00119         demandAttentionKNotifyTimer( NULL )
00120 // SELI do all as initialization
00121     {
00122 
00123     // set the initial mapping state
00124     mapping_state = WithdrawnState;
00125     desk = 0; // no desktop yet
00126 
00127     mode = PositionCenter;
00128     buttonDown = false;
00129     moveResizeMode = false;
00130 
00131     info = NULL;
00132 
00133     shade_mode = ShadeNone;
00134     active = false;
00135     deleting = false;
00136     keep_above = false;
00137     keep_below = false;
00138     motif_may_move = true;
00139     motif_may_resize = true;
00140     motif_may_close = true;
00141     fullscreen_mode = FullScreenNone;
00142     skip_taskbar = false;
00143     original_skip_taskbar = false;
00144     minimized = false;
00145     hidden = false;
00146     modal = false;
00147     noborder = false;
00148     app_noborder = false;
00149     urgency = false;
00150     ignore_focus_stealing = false;
00151     demands_attention = false;
00152     hidden_preview = false;
00153     raw_shown = false;
00154     check_active_modal = false;
00155 
00156     Pdeletewindow = 0;
00157     Ptakefocus = 0;
00158     Ptakeactivity = 0;
00159     Pcontexthelp = 0;
00160     Pping = 0;
00161     input = false;
00162     skip_pager = false;
00163 
00164     max_mode = MaximizeRestore;
00165     maxmode_restore = MaximizeRestore;
00166     
00167     cmap = None;
00168     
00169     geom = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0)
00170     client_size = QSize( 100, 100 );
00171 #if defined(HAVE_XSYNC) || defined(HAVE_XDAMAGE)
00172     ready_for_painting = false; // wait for first damage or sync reply
00173 #endif
00174 
00175     // SELI initialize xsizehints??
00176     }
00177 
00181 Client::~Client()
00182     {
00183 #ifdef HAVE_XSYNC
00184     if( sync_alarm != None )
00185         XSyncDestroyAlarm( display(), sync_alarm );
00186 #endif
00187     assert(!moveResizeMode);
00188     assert( client == None );
00189     assert( wrapper == None );
00190 //    assert( frameId() == None );
00191     assert( decoration == NULL );
00192     assert( block_geometry_updates == 0 );
00193     assert( !check_active_modal );
00194     delete bridge;
00195     }
00196 
00197 // use destroyClient() or releaseWindow(), Client instances cannot be deleted directly
00198 void Client::deleteClient( Client* c, allowed_t )
00199     {
00200     delete c;
00201     }
00202 
00206 void Client::releaseWindow( bool on_shutdown )
00207     {
00208     assert( !deleting );
00209     deleting = true;
00210     Deleted* del = Deleted::create( this );
00211     if( effects )
00212         {
00213         static_cast<EffectsHandlerImpl*>(effects)->windowClosed( effectWindow());
00214         scene->windowClosed( this, del );
00215         }
00216     finishCompositing();
00217     workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules
00218     StackingUpdatesBlocker blocker( workspace());
00219     if (moveResizeMode)
00220        leaveMoveResize();
00221     finishWindowRules();
00222     ++block_geometry_updates;
00223     if( isOnCurrentDesktop() && isShown( true ))
00224         addWorkspaceRepaint( geometry());
00225     // grab X during the release to make removing of properties, setting to withdrawn state
00226     // and repareting to root an atomic operation (http://lists.kde.org/?l=kde-devel&m=116448102901184&w=2)
00227     grabXServer();
00228     setMappingState( WithdrawnState );
00229     setModal( false ); // otherwise its mainwindow wouldn't get focus
00230     hidden = true; // so that it's not considered visible anymore (can't use hideClient(), it would set flags)
00231     if( !on_shutdown )
00232         workspace()->clientHidden( this );
00233     XUnmapWindow( display(), frameId()); // destroying decoration would cause ugly visual effect
00234     destroyDecoration();
00235     cleanGrouping();
00236     if( !on_shutdown )
00237         {
00238         workspace()->removeClient( this, Allowed );
00239         // only when the window is being unmapped, not when closing down KWin
00240         // (NETWM sections 5.5,5.7)
00241         info->setDesktop( 0 );
00242         desk = 0;
00243         info->setState( 0, info->state()); // reset all state flags
00244         }
00245     XDeleteProperty( display(), client, atoms->kde_net_wm_user_creation_time);
00246     XDeleteProperty( display(), client, atoms->net_frame_extents );
00247     XDeleteProperty( display(), client, atoms->kde_net_wm_frame_strut );
00248     XReparentWindow( display(), client, rootWindow(), x(), y());
00249     XRemoveFromSaveSet( display(), client );
00250     XSelectInput( display(), client, NoEventMask );
00251     if( on_shutdown )
00252         { // map the window, so it can be found after another WM is started
00253         XMapWindow( display(), client );
00254     // TODO preserve minimized, shaded etc. state?
00255         }
00256     else
00257         {
00258         // Make sure it's not mapped if the app unmapped it (#65279). The app
00259         // may do map+unmap before we initially map the window by calling rawShow() from manage().
00260         XUnmapWindow( display(), client ); 
00261         }
00262     client = None;
00263     XDestroyWindow( display(), wrapper );
00264     wrapper = None;
00265     XDestroyWindow( display(), frameId());
00266 //    frame = None;
00267     --block_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry
00268     disownDataPassedToDeleted();
00269     del->unrefWindow();
00270     checkNonExistentClients();
00271     deleteClient( this, Allowed );
00272     ungrabXServer();
00273     }
00274 
00275 // like releaseWindow(), but this one is called when the window has been already destroyed
00276 // (e.g. the application closed it)
00277 void Client::destroyClient()
00278     {
00279     assert( !deleting );
00280     deleting = true;
00281     Deleted* del = Deleted::create( this );
00282     if( effects )
00283         {
00284         static_cast<EffectsHandlerImpl*>(effects)->windowClosed( effectWindow());
00285         scene->windowClosed( this, del );
00286         }
00287     finishCompositing();
00288     workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules
00289     StackingUpdatesBlocker blocker( workspace());
00290     if (moveResizeMode)
00291        leaveMoveResize();
00292     finishWindowRules();
00293     ++block_geometry_updates;
00294     if( isOnCurrentDesktop() && isShown( true ))
00295         addWorkspaceRepaint( geometry());
00296     setModal( false );
00297     hidden = true; // so that it's not considered visible anymore
00298     workspace()->clientHidden( this );
00299     destroyDecoration();
00300     cleanGrouping();
00301     workspace()->removeClient( this, Allowed );
00302     client = None; // invalidate
00303     XDestroyWindow( display(), wrapper );
00304     wrapper = None;
00305     XDestroyWindow( display(), frameId());
00306 //    frame = None;
00307     --block_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry
00308     disownDataPassedToDeleted();
00309     del->unrefWindow();
00310     checkNonExistentClients();
00311     deleteClient( this, Allowed );
00312     }
00313 
00314 void Client::updateDecoration( bool check_workspace_pos, bool force )
00315     {
00316     if( !force && (( decoration == NULL && noBorder())
00317                     || ( decoration != NULL && !noBorder())))
00318         return;
00319     bool do_show = false;
00320     QRect oldgeom = geometry();
00321     blockGeometryUpdates( true );
00322     if( force )
00323         destroyDecoration();
00324     if( !noBorder())
00325         {
00326         setMask( QRegion()); // reset shape mask
00327         decoration = workspace()->createDecoration( bridge );
00328         // TODO check decoration's minimum size?
00329         decoration->init();
00330         decoration->widget()->installEventFilter( this );
00331         XReparentWindow( display(), decoration->widget()->winId(), frameId(), 0, 0 );
00332         decoration->widget()->lower();
00333         decoration->borders( border_left, border_right, border_top, border_bottom );
00334         int save_workarea_diff_x = workarea_diff_x;
00335         int save_workarea_diff_y = workarea_diff_y;
00336         move( calculateGravitation( false ));
00337         plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00338         workarea_diff_x = save_workarea_diff_x;
00339         workarea_diff_y = save_workarea_diff_y;
00340         do_show = true;
00341         if( compositing() )
00342             discardWindowPixmap();
00343         if( scene != NULL )
00344             scene->windowGeometryShapeChanged( this );
00345         if( effects != NULL )
00346             static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), oldgeom );
00347         }
00348     else
00349         destroyDecoration();
00350     if( check_workspace_pos )
00351         checkWorkspacePosition();
00352     blockGeometryUpdates( false );
00353     if( do_show )
00354         decoration->widget()->show();
00355     updateFrameExtents();
00356     }
00357 
00358 void Client::destroyDecoration()
00359     {
00360     QRect oldgeom = geometry();
00361     if( decoration != NULL )
00362         {
00363         delete decoration;
00364         decoration = NULL;
00365         QPoint grav = calculateGravitation( true );
00366         border_left = border_right = border_top = border_bottom = 0;
00367         setMask( QRegion()); // reset shape mask
00368         int save_workarea_diff_x = workarea_diff_x;
00369         int save_workarea_diff_y = workarea_diff_y;
00370         plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00371         move( grav );
00372         workarea_diff_x = save_workarea_diff_x;
00373         workarea_diff_y = save_workarea_diff_y;
00374         if( compositing() )
00375             discardWindowPixmap();
00376         if( scene != NULL && !deleting ) 
00377             scene->windowGeometryShapeChanged( this );
00378         if( effects != NULL && !deleting )
00379             static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), oldgeom );
00380         }
00381     }
00382 
00383 bool Client::checkBorderSizes( bool also_resize )
00384     {
00385     if( decoration == NULL )
00386         return false;
00387     int new_left, new_right, new_top, new_bottom;
00388     decoration->borders( new_left, new_right, new_top, new_bottom );
00389     if( new_left == border_left && new_right == border_right
00390         && new_top == border_top && new_bottom == border_bottom )
00391         return false;
00392     if( !also_resize )
00393         {
00394         border_left = new_left;
00395         border_right = new_right;
00396         border_top = new_top;
00397         border_bottom = new_bottom;
00398         return true;
00399         }
00400     GeometryUpdatesBlocker blocker( this );
00401     move( calculateGravitation( true ));
00402     border_left = new_left;
00403     border_right = new_right;
00404     border_top = new_top;
00405     border_bottom = new_bottom;
00406     move( calculateGravitation( false ));
00407     plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00408     checkWorkspacePosition();
00409     return true;
00410     }
00411 
00412 void Client::repaintDecoration()
00413     {
00414     if( decoration != NULL )
00415         decoration->widget()->update();
00416     }
00417 
00418 void Client::detectNoBorder()
00419     {
00420     if( shape())
00421         {
00422         noborder = true;
00423         app_noborder = true;
00424         return;
00425         }
00426     switch( windowType())
00427         {
00428         case NET::Desktop :
00429         case NET::Dock :
00430         case NET::TopMenu :
00431         case NET::Splash :
00432             noborder = true;
00433             app_noborder = true;
00434           break;
00435         case NET::Unknown :
00436         case NET::Normal :
00437         case NET::Toolbar :
00438         case NET::Menu :
00439         case NET::Dialog :
00440         case NET::Utility :
00441             noborder = false;
00442           break;
00443         default:
00444             assert( false );
00445         }
00446     // NET::Override is some strange beast without clear definition, usually
00447     // just meaning "noborder", so let's treat it only as such flag, and ignore it as
00448     // a window type otherwise (SUPPORTED_WINDOW_TYPES_MASK doesn't include it)
00449     if( info->windowType( SUPPORTED_MANAGED_WINDOW_TYPES_MASK | NET::OverrideMask ) == NET::Override )
00450         {
00451         noborder = true;
00452         app_noborder = true;
00453         }
00454     }
00455 
00456 void Client::updateFrameExtents()
00457     {
00458     NETStrut strut;
00459     strut.left = border_left;
00460     strut.right = border_right;
00461     strut.top = border_top;
00462     strut.bottom = border_bottom;
00463     info->setFrameExtents( strut );
00464     }
00465 
00466 // Resizes the decoration, and makes sure the decoration widget gets resize event
00467 // even if the size hasn't changed. This is needed to make sure the decoration
00468 // re-layouts (e.g. when options()->moveResizeMaximizedWindows() changes,
00469 // the decoration may turn on/off some borders, but the actual size
00470 // of the decoration stays the same).
00471 void Client::resizeDecoration( const QSize& s )
00472     {
00473     if( decoration == NULL )
00474         return;
00475     QSize oldsize = decoration->widget()->size();
00476     decoration->resize( s );
00477     if( oldsize == s )
00478         {
00479         QResizeEvent e( s, oldsize );
00480         QApplication::sendEvent( decoration->widget(), &e );
00481         }
00482     }
00483 
00484 bool Client::noBorder() const
00485     {
00486     return noborder || isFullScreen();
00487     }
00488 
00489 bool Client::userCanSetNoBorder() const
00490     {
00491     return !isFullScreen() && !isShade();
00492     }
00493 
00494 void Client::setNoBorder( bool set )
00495     {
00496     if( !userCanSetNoBorder())
00497         return;
00498     set = rules()->checkNoBorder( set );
00499     if( noborder == set )
00500         return;
00501     noborder = set;
00502     updateDecoration( true, false );
00503     updateWindowRules();
00504     }
00505 
00506 void Client::updateShape()
00507     {
00508     // workaround for #19644 - shaped windows shouldn't have decoration
00509     if( shape())
00510         {
00511         if( !app_noborder ) // only when shape is detected for the first time,
00512             {               // still let the user to override
00513             app_noborder = true;
00514             noborder = true;
00515             updateDecoration( true );
00516             }
00517         }
00518     if( shape() && noBorder())
00519         XShapeCombineShape( display(), frameId(), ShapeBounding,
00520             clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeSet );
00521     // Decoration mask (i.e. 'else' here) setting is done in setMask()
00522     // when the decoration calls it or when the decoration is created/destroyed
00523     updateInputShape();
00524     if( compositing())
00525         addDamageFull();
00526     if( scene != NULL )
00527         scene->windowGeometryShapeChanged( this );
00528     if( effects != NULL )
00529         static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), geometry());
00530     }
00531 
00532 static Window shape_helper_window = None;
00533 
00534 void Client::updateInputShape()
00535     {
00536     if( hidden_preview ) // sets it to none, don't change
00537         return;
00538     if( Extensions::shapeInputAvailable())
00539         { // There appears to be no way to find out if a window has input
00540           // shape set or not, so always propagate the input shape
00541           // (it's the same like the bounding shape by default).
00542           // Also, build the shape using a helper window, not directly
00543           // in the frame window, because the sequence set-shape-to-frame,
00544           // remove-shape-of-client, add-input-shape-of-client has the problem
00545           // that after the second step there's a hole in the input shape
00546           // until the real shape of the client is added and that can make
00547           // the window lose focus (which is a problem with mouse focus policies)
00548           // TODO it seems there is, after all - XShapeGetRectangles() - but maybe this is better
00549         if( shape_helper_window == None )
00550             shape_helper_window = XCreateSimpleWindow( display(), rootWindow(),
00551                 0, 0, 1, 1, 0, 0, 0 );
00552         XResizeWindow( display(), shape_helper_window, width(), height());
00553         XShapeCombineShape( display(), shape_helper_window, ShapeInput, 0, 0,
00554                            frameId(), ShapeBounding, ShapeSet );
00555         XShapeCombineShape( display(), shape_helper_window, ShapeInput,
00556                            clientPos().x(), clientPos().y(),
00557                            window(), ShapeBounding, ShapeSubtract );
00558         XShapeCombineShape( display(), shape_helper_window, ShapeInput,
00559                            clientPos().x(), clientPos().y(),
00560                            window(), ShapeInput, ShapeUnion );
00561         XShapeCombineShape( display(), frameId(), ShapeInput, 0, 0,
00562                            shape_helper_window, ShapeInput, ShapeSet );
00563         }
00564     }
00565 
00566 void Client::setMask( const QRegion& reg, int mode )
00567     {
00568     if( _mask == reg )
00569         return;
00570     _mask = reg;
00571     Window shape_window = frameId();
00572     if( shape())
00573         {
00574         // the same way of applying a shape without strange intermediate states like above
00575         if( shape_helper_window == None )
00576             shape_helper_window = XCreateSimpleWindow( display(), rootWindow(),
00577                 0, 0, 1, 1, 0, 0, 0 );
00578         shape_window = shape_helper_window;
00579         }
00580     if( reg.isEmpty())
00581         XShapeCombineMask( display(), shape_window, ShapeBounding, 0, 0,
00582             None, ShapeSet );
00583     else if( mode == X::Unsorted )
00584         XShapeCombineRegion( display(), shape_window, ShapeBounding, 0, 0,
00585             reg.handle(), ShapeSet );
00586     else
00587         {
00588         QVector< QRect > rects = reg.rects();
00589         XRectangle* xrects = new XRectangle[ rects.count() ];
00590         for( int i = 0;
00591              i < rects.count();
00592              ++i )
00593             {
00594             xrects[ i ].x = rects[ i ].x();
00595             xrects[ i ].y = rects[ i ].y();
00596             xrects[ i ].width = rects[ i ].width();
00597             xrects[ i ].height = rects[ i ].height();
00598             }
00599         XShapeCombineRectangles( display(), shape_window, ShapeBounding, 0, 0,
00600             xrects, rects.count(), ShapeSet, mode );
00601         delete[] xrects;
00602         }
00603     if( shape())
00604         { // the rest of the applyign using a temporary window
00605         XRectangle rec = { 0, 0, clientSize().width(), clientSize().height() };
00606         XShapeCombineRectangles( display(), shape_helper_window, ShapeBounding,
00607                            clientPos().x(), clientPos().y(), &rec, 1, ShapeSubtract, Unsorted );
00608         XShapeCombineShape( display(), shape_helper_window, ShapeBounding,
00609                            clientPos().x(), clientPos().y(),
00610                            window(), ShapeBounding, ShapeUnion );
00611         XShapeCombineShape( display(), frameId(), ShapeBounding, 0, 0,
00612                            shape_helper_window, ShapeBounding, ShapeSet );
00613         }
00614     if( compositing())
00615         addDamageFull();
00616     if( scene != NULL )
00617         scene->windowGeometryShapeChanged( this );
00618     if( effects != NULL )
00619         static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), geometry());
00620     updateShape();
00621     }
00622 
00623 QRegion Client::mask() const
00624     {
00625     if( _mask.isEmpty())
00626         return QRegion( 0, 0, width(), height());
00627     return _mask;
00628     }
00629     
00630 void Client::hideClient( bool hide )
00631     {
00632     if( hidden == hide )
00633         return;
00634     hidden = hide;
00635     updateVisibility();
00636     }
00637     
00638 /*
00639   Returns whether the window is minimizable or not
00640  */
00641 bool Client::isMinimizable() const
00642     {
00643     if( isSpecialWindow())
00644         return false;
00645     if( isTransient())
00646         { // #66868 - let other xmms windows be minimized when the mainwindow is minimized
00647         bool shown_mainwindow = false;
00648         ClientList mainclients = mainClients();
00649         for( ClientList::ConstIterator it = mainclients.begin();
00650              it != mainclients.end();
00651              ++it )
00652             {
00653             if( (*it)->isShown( true ))
00654                 shown_mainwindow = true;
00655             }
00656         if( !shown_mainwindow )
00657             return true;
00658         }
00659     // this is here because kicker's taskbar doesn't provide separate entries
00660     // for windows with an explicitly given parent
00661     // TODO perhaps this should be redone
00662     if( transientFor() != NULL )
00663         return false;
00664     if( !wantsTabFocus()) // SELI - NET::Utility? why wantsTabFocus() - skiptaskbar? ?
00665         return false;
00666     return true;
00667     }
00668 
00672 void Client::minimize( bool avoid_animation )
00673     {
00674     if ( !isMinimizable() || isMinimized())
00675         return;
00676 
00677     Notify::raise( Notify::Minimize );
00678 
00679     minimized = true;
00680 
00681     updateVisibility();
00682     updateAllowedActions();
00683     workspace()->updateMinimizedOfTransients( this );
00684     updateWindowRules();
00685     workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast );
00686     if( effects && !avoid_animation ) // TODO shouldn't it tell effects at least about the change?
00687         static_cast<EffectsHandlerImpl*>(effects)->windowMinimized( effectWindow());
00688     }
00689 
00690 void Client::unminimize( bool avoid_animation )
00691     {
00692     if( !isMinimized())
00693         return;
00694 
00695     Notify::raise( Notify::UnMinimize );
00696     minimized = false;
00697     updateVisibility();
00698     updateAllowedActions();
00699     workspace()->updateMinimizedOfTransients( this );
00700     updateWindowRules();
00701     if( effects && !avoid_animation )
00702         static_cast<EffectsHandlerImpl*>(effects)->windowUnminimized( effectWindow());
00703     }
00704 
00705 QRect Client::iconGeometry() const
00706     {
00707     NETRect r = info->iconGeometry();
00708     QRect geom( r.pos.x, r.pos.y, r.size.width, r.size.height );
00709     if( geom.isValid() )
00710         return geom;
00711     else
00712     {
00713         // Check all mainwindows of this window (recursively)
00714         foreach( Client* mainwin, mainClients() )
00715             {
00716             geom = mainwin->iconGeometry();
00717             if( geom.isValid() )
00718                 return geom;
00719             }
00720         // No mainwindow (or their parents) with icon geometry was found
00721         return QRect();
00722         }
00723     }
00724 
00725 bool Client::isShadeable() const
00726     {
00727     return !isSpecialWindow() && !noBorder();
00728     }
00729 
00730 void Client::setShade( ShadeMode mode )
00731     {
00732     if( !isShadeable())
00733         return;
00734     mode = rules()->checkShade( mode );
00735     if( shade_mode == mode )
00736         return;
00737     bool was_shade = isShade();
00738     ShadeMode was_shade_mode = shade_mode;
00739     shade_mode = mode;
00740     if( was_shade == isShade())
00741         {
00742         if( decoration != NULL ) // decoration may want to update after e.g. hover-shade changes
00743             decoration->shadeChange();
00744         return; // no real change in shaded state
00745         }
00746 
00747     if( shade_mode == ShadeNormal )
00748         {
00749         if ( isShown( true ) && isOnCurrentDesktop())
00750                 Notify::raise( Notify::ShadeUp );
00751         }
00752     else if( shade_mode == ShadeNone )
00753         {
00754         if( isShown( true ) && isOnCurrentDesktop())
00755                 Notify::raise( Notify::ShadeDown );
00756         }
00757 
00758     assert( decoration != NULL ); // noborder windows can't be shaded
00759     GeometryUpdatesBlocker blocker( this );
00760     // decorations may turn off some borders when shaded
00761     decoration->borders( border_left, border_right, border_top, border_bottom );
00762 
00763 // TODO all this unmapping, resizing etc. feels too much duplicated from elsewhere
00764     if ( isShade()) 
00765         { // shade_mode == ShadeNormal
00766         addWorkspaceRepaint( geometry());
00767         // shade
00768         shade_geometry_change = true;
00769         QSize s( sizeForClientSize( QSize( clientSize())));
00770         s.setHeight( border_top + border_bottom );
00771         XSelectInput( display(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
00772         XUnmapWindow( display(), wrapper );
00773         XUnmapWindow( display(), client );
00774         XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask );
00775         plainResize( s );
00776         shade_geometry_change = false;
00777         if( isActive())
00778             {
00779             if( was_shade_mode == ShadeHover )
00780                 workspace()->activateNextClient( this );
00781             else
00782                 workspace()->focusToNull();
00783             }
00784         }
00785     else 
00786         {
00787         shade_geometry_change = true;
00788         QSize s( sizeForClientSize( clientSize()));
00789         shade_geometry_change = false;
00790         plainResize( s );
00791         if( shade_mode == ShadeHover || shade_mode == ShadeActivated )
00792             setActive( true );
00793         XMapWindow( display(), wrapperId());
00794         XMapWindow( display(), window());
00795         if ( isActive() )
00796             workspace()->requestFocus( this );
00797         }
00798     checkMaximizeGeometry();
00799     info->setState( isShade() ? NET::Shaded : 0, NET::Shaded );
00800     info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden );
00801     discardWindowPixmap();
00802     updateVisibility();
00803     updateAllowedActions();
00804     workspace()->updateMinimizedOfTransients( this );
00805     decoration->shadeChange();
00806     updateWindowRules();
00807     }
00808 
00809 void Client::shadeHover()
00810     {
00811     setShade( ShadeHover );
00812     cancelShadeHover();
00813     }
00814 
00815 void Client::cancelShadeHover()
00816     {
00817     delete shadeHoverTimer;
00818     shadeHoverTimer = 0;
00819     }
00820 
00821 void Client::toggleShade()
00822     {
00823     // if the mode is ShadeHover or ShadeActive, cancel shade too
00824     setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone );
00825     }
00826 
00827 void Client::updateVisibility()
00828     {
00829     if( deleting )
00830         return;
00831     bool show = true;
00832     if( hidden )
00833         {
00834         setMappingState( IconicState );
00835         info->setState( NET::Hidden, NET::Hidden );
00836         setSkipTaskbar( true, false ); // also hide from taskbar
00837         rawHide();
00838         show = false;
00839         }
00840     else
00841         {
00842         setSkipTaskbar( original_skip_taskbar, false );
00843         }
00844     if( minimized )
00845         {
00846         setMappingState( IconicState );
00847         info->setState( NET::Hidden, NET::Hidden );
00848         rawHide();
00849         show = false;
00850         }
00851     if( show )
00852         info->setState( 0, NET::Hidden );
00853     if( !isOnCurrentDesktop())
00854         {
00855         setMappingState( IconicState );
00856         rawHide();
00857         show = false;
00858         }
00859     if( show )
00860         {
00861         bool belongs_to_desktop = false;
00862         for( ClientList::ConstIterator it = group()->members().begin();
00863              it != group()->members().end();
00864              ++it )
00865             if( (*it)->isDesktop())
00866                 {
00867                 belongs_to_desktop = true;
00868                 break;
00869                 }
00870         if( !belongs_to_desktop && workspace()->showingDesktop())
00871             workspace()->resetShowingDesktop( true );
00872         if( isShade())
00873             setMappingState( IconicState );
00874         else
00875             setMappingState( NormalState );
00876         rawShow();
00877         }
00878     }
00879 
00884 void Client::setMappingState(int s)
00885     {
00886     assert( client != None );
00887     assert( !deleting || s == WithdrawnState );
00888     if( mapping_state == s )
00889         return;
00890     bool was_unmanaged = ( mapping_state == WithdrawnState );
00891     mapping_state = s;
00892     if( mapping_state == WithdrawnState )
00893         {
00894         XDeleteProperty( display(), window(), atoms->wm_state );
00895         return;
00896         }
00897     assert( s == NormalState || s == IconicState );
00898 
00899     unsigned long data[2];
00900     data[0] = (unsigned long) s;
00901     data[1] = (unsigned long) None;
00902     XChangeProperty(display(), window(), atoms->wm_state, atoms->wm_state, 32,
00903         PropModeReplace, (unsigned char *)data, 2);
00904 
00905     if( was_unmanaged ) // manage() did block_geometry_updates = 1, now it's ok to finally set the geometry
00906         blockGeometryUpdates( false );
00907     }
00908 
00913 void Client::rawShow()
00914     {
00915     if( raw_shown ) // this flag is used to purely avoid repeated calls to rawShow(),
00916         return;     // it doesn't say anything more about the state
00917     raw_shown = true;
00918     if( decoration != NULL )
00919         decoration->widget()->show(); // not really necessary, but let it know the state
00920     XMapWindow( display(), frameId());
00921     if( !isShade())
00922         {
00923         XMapWindow( display(), wrapper );
00924         XMapWindow( display(), client );
00925         }
00926     if( options->hiddenPreviews == HiddenPreviewsNever )
00927         {
00928         // XComposite invalidates backing pixmaps on unmap (minimize, different
00929         // virtual desktop, etc.).  We kept the last known good pixmap around
00930         // for use in effects, but now we want to have access to the new pixmap
00931         if( compositing() )
00932             discardWindowPixmap();
00933         }
00934     else
00935         {
00936         if( hidden_preview )
00937             setHiddenPreview( false, Allowed );
00938         }
00939     }
00940 
00946 void Client::rawHide()
00947     {
00948     if( !raw_shown )
00949         return;
00950     raw_shown = false;
00951     StackingUpdatesBlocker blocker( workspace());
00952     addWorkspaceRepaint( geometry());
00953     if( options->hiddenPreviews == HiddenPreviewsNever )
00954         {
00955         // Here it may look like a race condition, as some other client might try to unmap
00956         // the window between these two XSelectInput() calls. However, they're supposed to
00957         // use XWithdrawWindow(), which also sends a synthetic event to the root window,
00958         // which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify
00959         // will be missed is also very minimal, so I don't think it's needed to grab the server
00960         // here.
00961             XSelectInput( display(), wrapper, ClientWinMask ); // avoid getting UnmapNotify
00962             XUnmapWindow( display(), frameId());
00963             XUnmapWindow( display(), wrapper );
00964             XUnmapWindow( display(), client );
00965             XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask );
00966             if( decoration != NULL )
00967                 decoration->widget()->hide(); // not really necessary, but let it know the state
00968         }
00969     else
00970         {
00971         if( !hidden_preview )
00972             {
00973             setHiddenPreview( true, Allowed );
00974             // actually keep the window mapped (taken from rawShow())
00975             if( decoration != NULL )
00976                 decoration->widget()->show(); // not really necessary, but let it know the state
00977             XMapWindow( display(), frameId());
00978             if( !isShade())
00979                 {
00980                 XMapWindow( display(), wrapper );
00981                 XMapWindow( display(), client );
00982                 }
00983             }
00984         }
00985     workspace()->clientHidden( this );
00986     }
00987 
00988 // XComposite doesn't keep window pixmaps of unmapped windows, which means
00989 // there wouldn't be any previews of windows that are minimized or on another
00990 // virtual desktop. Therefore rawHide() actually keeps such windows mapped.
00991 // However special care needs to be taken so that such windows don't interfere.
00992 // Therefore they're put very low in the stacking order and they have input shape
00993 // set to none, which hopefully is enough. If there's no input shape available,
00994 // then it's hoped that there will be some other desktop above it *shrug*.
00995 // Using normal shape would be better, but that'd affect other things, e.g. painting
00996 // of the actual preview.
00997 void Client::setHiddenPreview( bool set, allowed_t )
00998     {
00999     if( set && !hidden_preview )
01000         { // set
01001         hidden_preview = true;
01002         workspace()->forceRestacking();
01003         if( Extensions::shapeInputAvailable())
01004             XShapeCombineRectangles( display(), frameId(), ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted );
01005         }
01006     else if( !set && hidden_preview )
01007         { // unset
01008         hidden_preview = false;
01009         workspace()->forceRestacking();
01010         updateInputShape();
01011         }
01012     }
01013 
01014 void Client::sendClientMessage(Window w, Atom a, Atom protocol, long data1, long data2, long data3)
01015     {
01016     XEvent ev;
01017     long mask;
01018 
01019     memset(&ev, 0, sizeof(ev));
01020     ev.xclient.type = ClientMessage;
01021     ev.xclient.window = w;
01022     ev.xclient.message_type = a;
01023     ev.xclient.format = 32;
01024     ev.xclient.data.l[0] = protocol;
01025     ev.xclient.data.l[1] = xTime();
01026     ev.xclient.data.l[2] = data1;
01027     ev.xclient.data.l[3] = data2;
01028     ev.xclient.data.l[4] = data3;
01029     mask = 0L;
01030     if (w == rootWindow())
01031       mask = SubstructureRedirectMask;        /* magic! */
01032     XSendEvent(display(), w, False, mask, &ev);
01033     }
01034 
01035 /*
01036   Returns whether the window may be closed (have a close button)
01037  */
01038 bool Client::isCloseable() const
01039     {
01040     return rules()->checkCloseable( motif_may_close && !isSpecialWindow());
01041     }
01042 
01047 void Client::closeWindow()
01048     {
01049     if( !isCloseable())
01050         return;
01051     // Update user time, because the window may create a confirming dialog.
01052     updateUserTime(); 
01053     if ( Pdeletewindow )
01054         {
01055         Notify::raise( Notify::Close );
01056         sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window);
01057         pingWindow();
01058         }
01059     else 
01060         {
01061         // client will not react on wm_delete_window. We have not choice
01062         // but destroy his connection to the XServer.
01063         killWindow();
01064         }
01065     }
01066 
01067 
01071 void Client::killWindow()
01072     {
01073     kDebug( 1212 ) << "Client::killWindow():" << caption();
01074     // not sure if we need an Notify::Kill or not.. until then, use
01075     // Notify::Close
01076     Notify::raise( Notify::Close );
01077 
01078     if( isDialog())
01079         Notify::raise( Notify::TransDelete );
01080     if( isNormalWindow())
01081         Notify::raise( Notify::Delete );
01082     killProcess( false );
01083     // always kill this client at the server
01084     XKillClient(display(), window() );
01085     destroyClient();
01086     }
01087 
01088 // send a ping to the window using _NET_WM_PING if possible
01089 // if it doesn't respond within a reasonable time, it will be
01090 // killed
01091 void Client::pingWindow()
01092     {
01093     if( !Pping )
01094         return; // can't ping :(
01095     if( options->killPingTimeout == 0 )
01096         return; // turned off
01097     if( ping_timer != NULL )
01098         return; // pinging already
01099     ping_timer = new QTimer( this );
01100     connect( ping_timer, SIGNAL( timeout()), SLOT( pingTimeout()));
01101     ping_timer->setSingleShot( true );
01102     ping_timer->start( options->killPingTimeout );
01103     ping_timestamp = xTime();
01104     workspace()->sendPingToWindow( window(), ping_timestamp );
01105     }
01106 
01107 void Client::gotPing( Time timestamp )
01108     {
01109     // just plain compare is not good enough because of 64bit and truncating and whatnot
01110     if( NET::timestampCompare( timestamp, ping_timestamp ) != 0 )
01111         return;
01112     delete ping_timer;
01113     ping_timer = NULL;
01114     if( process_killer != NULL )
01115         {
01116         process_killer->kill();
01117         // recycle when the process manager has noticed that the process exited
01118         // a delete process_killer here sometimes causes a hang in waitForFinished
01119         connect(process_killer, SIGNAL(finished(int, QProcess::ExitStatus)), process_killer,
01120                 SLOT(deleteLater()));
01121         process_killer = NULL;
01122         }
01123     }
01124 
01125 void Client::pingTimeout()
01126     {
01127     kDebug( 1212 ) << "Ping timeout:" << caption();
01128     ping_timer->deleteLater();
01129     ping_timer = NULL;
01130     killProcess( true, ping_timestamp );
01131     }
01132 
01133 void Client::killProcess( bool ask, Time timestamp )
01134     {
01135     if( process_killer != NULL )
01136         return;
01137     Q_ASSERT( !ask || timestamp != CurrentTime );
01138     QByteArray machine = wmClientMachine( true );
01139     pid_t pid = info->pid();
01140     if( pid <= 0 || machine.isEmpty()) // needed properties missing
01141         return;
01142     kDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")";
01143     if( !ask )
01144         {
01145         if( machine != "localhost" )
01146             {
01147                   QStringList lst;
01148                   lst << machine << "kill" << QString::number( pid );
01149                   QProcess::startDetached("xon",lst);
01150             }
01151         else
01152             ::kill( pid, SIGTERM );
01153         }
01154     else
01155         {
01156         process_killer = new QProcess( this );
01157         connect( process_killer, SIGNAL( error( QProcess::ProcessError )), SLOT( processKillerExited()));
01158         connect( process_killer, SIGNAL( finished( int, QProcess::ExitStatus )), SLOT( processKillerExited()));
01159         process_killer->start( KStandardDirs::findExe( "kwin_killer_helper" ),
01160             QStringList() << "--pid" << QByteArray().setNum( (unsigned)pid ) << "--hostname" << machine
01161             << "--windowname" << caption()
01162             << "--applicationname" << resourceClass()
01163             << "--wid" << QString::number( window() )
01164             << "--timestamp" << QString::number( timestamp ));
01165         }
01166     }
01167 
01168 void Client::processKillerExited()
01169     {
01170     kDebug( 1212 ) << "Killer exited";
01171     delete process_killer;
01172     process_killer = NULL;
01173     }
01174 
01175 void Client::setSkipTaskbar( bool b, bool from_outside )
01176     {
01177     int was_wants_tab_focus = wantsTabFocus();
01178     if( from_outside )
01179         {
01180         b = rules()->checkSkipTaskbar( b );
01181         original_skip_taskbar = b;
01182         }
01183     if ( b == skipTaskbar() )
01184         return;
01185     skip_taskbar = b;
01186     info->setState( b?NET::SkipTaskbar:0, NET::SkipTaskbar );
01187     updateWindowRules();
01188     if( was_wants_tab_focus != wantsTabFocus())
01189         workspace()->updateFocusChains( this,
01190             isActive() ? Workspace::FocusChainMakeFirst : Workspace::FocusChainUpdate );
01191     }
01192 
01193 void Client::setSkipPager( bool b )
01194     {
01195     b = rules()->checkSkipPager( b );
01196     if ( b == skipPager() )
01197         return;
01198     skip_pager = b;
01199     info->setState( b?NET::SkipPager:0, NET::SkipPager );
01200     updateWindowRules();
01201     }
01202 
01203 void Client::setModal( bool m )
01204     { // Qt-3.2 can have even modal normal windows :(
01205     if( modal == m )
01206         return;
01207     modal = m;
01208     if( !modal )
01209         return;
01210     // changing modality for a mapped window is weird (?)
01211     // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG
01212     }
01213 
01214 void Client::setDesktop( int desktop )
01215     {
01216     if( desktop != NET::OnAllDesktops ) // do range check
01217         desktop = qMax( 1, qMin( workspace()->numberOfDesktops(), desktop ));
01218     desktop = rules()->checkDesktop( desktop );
01219     if( desk == desktop )
01220         return;
01221     int was_desk = desk;
01222     desk = desktop;
01223     info->setDesktop( desktop );
01224     if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops ))
01225         { // onAllDesktops changed
01226         if ( isShown( true ))
01227             Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops );
01228         workspace()->updateOnAllDesktopsOfTransients( this );
01229         }
01230     if( decoration != NULL )
01231         decoration->desktopChange();
01232     workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst );
01233     updateVisibility();
01234     updateWindowRules();
01235     }
01236 
01243 int Client::desktop() const
01244     {
01245     return desk;
01246     }
01247 
01248 void Client::setOnAllDesktops( bool b )
01249     {
01250     if(( b && isOnAllDesktops())
01251         || ( !b && !isOnAllDesktops()))
01252         return;
01253     if( b )
01254         setDesktop( NET::OnAllDesktops );
01255     else
01256         setDesktop( workspace()->currentDesktop());
01257     }
01258 
01259 // performs activation and/or raising of the window
01260 void Client::takeActivity( int flags, bool handled, allowed_t )
01261     {
01262     if( !handled || !Ptakeactivity )
01263         {
01264         if( flags & ActivityFocus )
01265             takeFocus( Allowed );
01266         if( flags & ActivityRaise )
01267             workspace()->raiseClient( this );
01268         return;
01269         }
01270 
01271 #ifndef NDEBUG
01272     static Time previous_activity_timestamp;
01273     static Client* previous_client;
01274 #if 0
01275     if( previous_activity_timestamp == xTime() && previous_client != this )
01276         {
01277         kDebug( 1212 ) << "Repeated use of the same X timestamp for activity";
01278         kDebug( 1212 ) << kBacktrace();
01279         }
01280 #endif
01281     previous_activity_timestamp = xTime();
01282     previous_client = this;
01283 #endif
01284     workspace()->sendTakeActivity( this, xTime(), flags );
01285     }
01286 
01287 // performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS
01288 void Client::takeFocus( allowed_t )
01289     {
01290 #ifndef NDEBUG
01291     static Time previous_focus_timestamp;
01292     static Client* previous_client;
01293 #if 0
01294     if( previous_focus_timestamp == xTime() && previous_client != this )
01295         {
01296         kDebug( 1212 ) << "Repeated use of the same X timestamp for focus";
01297         kDebug( 1212 ) << kBacktrace();
01298         }
01299 #endif
01300     previous_focus_timestamp = xTime();
01301     previous_client = this;
01302 #endif
01303     if ( rules()->checkAcceptFocus( input ))
01304         {
01305         XSetInputFocus( display(), window(), RevertToPointerRoot, xTime() );
01306         }
01307     if ( Ptakefocus )
01308         sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus);
01309     workspace()->setShouldGetFocus( this );
01310     }
01311 
01319 bool Client::providesContextHelp() const
01320     {
01321     return Pcontexthelp;
01322     }
01323 
01324 
01331 void Client::showContextHelp()
01332     {
01333     if ( Pcontexthelp ) 
01334         {
01335         sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help);
01336         QWhatsThis::enterWhatsThisMode(); // SELI?
01337         }
01338     }
01339 
01340 
01345 void Client::fetchName()
01346     {
01347     setCaption( readName());
01348     }
01349 
01350 QString Client::readName() const
01351     {
01352     if ( info->name() && info->name()[ 0 ] != '\0' ) 
01353         return QString::fromUtf8( info->name() );
01354     else 
01355         return KWindowSystem::readNameProperty( window(), XA_WM_NAME );
01356     }
01357     
01358 KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, Client, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption());
01359 
01360 void Client::setCaption( const QString& _s, bool force )
01361     {
01362     QString s = _s;
01363     if ( s != cap_normal || force ) 
01364         {
01365         bool reset_name = force;
01366         for( int i = 0;
01367              i < s.length();
01368              ++i )
01369             if( !s[ i ].isPrint())
01370                 s[ i ] = QChar( ' ' );
01371         cap_normal = s;
01372         bool was_suffix = ( !cap_suffix.isEmpty());
01373         QString machine_suffix;
01374         if( wmClientMachine( false ) != "localhost" && !isLocalMachine( wmClientMachine( false )))
01375             machine_suffix = " <@" + wmClientMachine( true ) + '>';
01376         QString shortcut_suffix = !shortcut().isEmpty() ? ( " {" + shortcut().toString() + '}' ) : QString();
01377         cap_suffix = machine_suffix + shortcut_suffix;
01378         if ( ( !isSpecialWindow() || isToolbar()) && workspace()->findClient( FetchNameInternalPredicate( this ))) 
01379             {
01380             int i = 2;
01381             do 
01382                 {
01383                 cap_suffix = machine_suffix + " <" + QString::number(i) + '>' + shortcut_suffix;
01384                 i++;
01385                 } while ( workspace()->findClient( FetchNameInternalPredicate( this )));
01386             info->setVisibleName( caption().toUtf8() );
01387             reset_name = false;
01388             }
01389         if(( was_suffix && cap_suffix.isEmpty()
01390             || reset_name )) // if it was new window, it may have old value still set, if the window is reused
01391             {
01392             info->setVisibleName( "" ); // remove
01393             info->setVisibleIconName( "" ); // remove
01394             }
01395         else if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set
01396             info->setVisibleIconName( ( cap_iconic + cap_suffix ).toUtf8() );
01397 
01398         if( isManaged() && decoration != NULL )
01399                 decoration->captionChange();
01400         }
01401     }
01402 
01403 void Client::updateCaption()
01404     {
01405     setCaption( cap_normal, true );
01406     }
01407 
01408 void Client::fetchIconicName()
01409     {
01410     QString s;
01411     if ( info->iconName() && info->iconName()[ 0 ] != '\0' ) 
01412         s = QString::fromUtf8( info->iconName() );
01413     else 
01414         s = KWindowSystem::readNameProperty( window(), XA_WM_ICON_NAME );
01415     if ( s != cap_iconic ) 
01416         {
01417     bool was_set = !cap_iconic.isEmpty();
01418         cap_iconic = s;
01419         if( !cap_suffix.isEmpty())
01420         {
01421         if( !cap_iconic.isEmpty()) // keep the same suffix in iconic name if it's set
01422             info->setVisibleIconName( ( s + cap_suffix ).toUtf8() );
01423         else if( was_set )
01424         info->setVisibleIconName( "" ); //remove
01425         }
01426         }
01427     }
01428 
01431 QString Client::caption( bool full ) const
01432     {
01433     return full ? cap_normal + cap_suffix : cap_normal;
01434     }
01435 
01436 void Client::getWMHints()
01437     {
01438     XWMHints *hints = XGetWMHints(display(), window() );
01439     input = true;
01440     window_group = None;
01441     urgency = false;
01442     if ( hints )
01443         {
01444         if( hints->flags & InputHint )
01445             input = hints->input;
01446         if( hints->flags & WindowGroupHint )
01447             window_group = hints->window_group;
01448         urgency = ( hints->flags & UrgencyHint ) ? true : false; // true/false needed, it's uint bitfield
01449         XFree( (char*)hints );
01450         }
01451     checkGroup();
01452     updateUrgency();
01453     updateAllowedActions(); // group affects isMinimizable()
01454     }
01455 
01456 void Client::getMotifHints()
01457     {
01458     bool mnoborder, mresize, mmove, mminimize, mmaximize, mclose;
01459     Motif::readFlags( client, mnoborder, mresize, mmove, mminimize, mmaximize, mclose );
01460     if( mnoborder )
01461         {
01462         noborder = true;
01463         app_noborder =  true;
01464         }
01465     if( !hasNETSupport()) // NETWM apps should set type and size constraints
01466         {
01467         motif_may_resize = mresize; // this should be set using minsize==maxsize, but oh well
01468         motif_may_move = mmove;
01469         }
01470     else
01471         motif_may_resize = motif_may_move = true;
01472     // mminimize; - ignore, bogus - e.g. shading or sending to another desktop is "minimizing" too
01473     // mmaximize; - ignore, bogus - maximizing is basically just resizing
01474     motif_may_close = mclose; // motif apps like to crash when they set this hint and WM closes them anyway
01475     if( isManaged())
01476         updateDecoration( true ); // check if noborder state has changed
01477     }
01478 
01479 void Client::readIcons( Window win, QPixmap* icon, QPixmap* miniicon )
01480     {    
01481     // get the icons, allow scaling
01482     if( icon != NULL )
01483         *icon = KWindowSystem::icon( win, 32, 32, true, KWindowSystem::NETWM | KWindowSystem::WMHints );
01484     if( miniicon != NULL )
01485         if( icon == NULL || !icon->isNull())
01486             *miniicon = KWindowSystem::icon( win, 16, 16, true, KWindowSystem::NETWM | KWindowSystem::WMHints );
01487         else
01488             *miniicon = QPixmap();
01489     }
01490 
01491 void Client::getIcons()
01492     {
01493     // first read icons from the window itself
01494     readIcons( window(), &icon_pix, &miniicon_pix );
01495     if( icon_pix.isNull())
01496         { // then try window group
01497         icon_pix = group()->icon();
01498         miniicon_pix = group()->miniIcon();
01499         }
01500     if( icon_pix.isNull() && isTransient())
01501         { // then mainclients
01502         ClientList mainclients = mainClients();
01503         for( ClientList::ConstIterator it = mainclients.begin();
01504              it != mainclients.end() && icon_pix.isNull();
01505              ++it )
01506             {
01507             icon_pix = (*it)->icon();
01508             miniicon_pix = (*it)->miniIcon();
01509             }
01510         }
01511     if( icon_pix.isNull())
01512         { // and if nothing else, load icon from classhint or xapp icon
01513         icon_pix = KWindowSystem::icon( window(), 32, 32, true, KWindowSystem::ClassHint | KWindowSystem::XApp );
01514         miniicon_pix = KWindowSystem::icon( window(), 16, 16, true, KWindowSystem::ClassHint | KWindowSystem::XApp );
01515         }
01516     if( isManaged() && decoration != NULL )
01517         decoration->iconChange();
01518     }
01519 
01520 void Client::getWindowProtocols()
01521     {
01522     Atom *p;
01523     int i,n;
01524 
01525     Pdeletewindow = 0;
01526     Ptakefocus = 0;
01527     Ptakeactivity = 0;
01528     Pcontexthelp = 0;
01529     Pping = 0;
01530 
01531     if (XGetWMProtocols(display(), window(), &p, &n))
01532         {
01533         for (i = 0; i < n; i++)
01534             if (p[i] == atoms->wm_delete_window)
01535                 Pdeletewindow = 1;
01536             else if (p[i] == atoms->wm_take_focus)
01537                 Ptakefocus = 1;
01538             else if (p[i] == atoms->net_wm_take_activity)
01539                 Ptakeactivity = 1;
01540             else if (p[i] == atoms->net_wm_context_help)
01541                 Pcontexthelp = 1;
01542             else if (p[i] == atoms->net_wm_ping)
01543                 Pping = 1;
01544         if (n>0)
01545             XFree(p);
01546         }
01547     }
01548 
01549 void Client::getSyncCounter() 
01550 {
01551 #ifdef HAVE_XSYNC
01552     if( !Extensions::syncAvailable())
01553         return;
01554 
01555     Atom retType;
01556     unsigned long nItemRet;
01557     unsigned long byteRet;
01558     int formatRet;
01559     unsigned char* propRet;
01560     int ret = XGetWindowProperty( display(), window(), atoms->net_wm_sync_request_counter,
01561         0, 1, false, XA_CARDINAL, &retType, &formatRet, &nItemRet, &byteRet, &propRet );
01562     
01563     if( ret == Success && formatRet == 32 )
01564         {
01565         sync_counter = *(long*)propRet;
01566         XSyncIntToValue( &sync_counter_value, 0 );
01567     XSyncValue zero;
01568     XSyncIntToValue( &zero, 0 );
01569     XSyncSetCounter( display(), sync_counter, zero );
01570         if( sync_alarm == None )
01571             {
01572             XSyncAlarmAttributes attrs;
01573             attrs.trigger.counter = sync_counter;
01574             attrs.trigger.value_type = XSyncRelative;
01575             attrs.trigger.test_type = XSyncPositiveTransition;
01576             XSyncIntToValue( &attrs.trigger.wait_value, 1 );
01577             XSyncIntToValue( &attrs.delta, 1 );
01578             sync_alarm = XSyncCreateAlarm( display(),
01579                 XSyncCACounter | XSyncCAValueType | XSyncCATestType | XSyncCADelta | XSyncCAValue,
01580                 &attrs );  
01581             }
01582         }
01583 #endif
01584 }
01585 
01586 // send the client a _NET_SYNC_REQUEST
01587 void Client::sendSyncRequest()
01588 {
01589 #ifdef HAVE_XSYNC
01590     if( sync_counter == None )
01591         return;
01592 
01593     // we increment before the notify so that after the notify
01594     // syncCounterSerial will equal the value we are expecting
01595     // in the acknowledgement
01596     int overflow;
01597     XSyncValue one;
01598     XSyncIntToValue( &one, 1 );
01599 #undef XSyncValueAdd // it causes a warning :-/
01600     XSyncValueAdd( &sync_counter_value, sync_counter_value, one, &overflow );
01601 
01602     // send the message to client
01603     XEvent ev;
01604     ev.xclient.type = ClientMessage;
01605     ev.xclient.window = window();
01606     ev.xclient.format = 32;
01607     ev.xclient.message_type = atoms->wm_protocols;
01608     ev.xclient.data.l[ 0 ] = atoms->net_wm_sync_request;
01609     ev.xclient.data.l[ 1 ] = xTime();
01610     ev.xclient.data.l[ 2 ] = XSyncValueLow32( sync_counter_value );
01611     ev.xclient.data.l[ 3 ] = XSyncValueHigh32( sync_counter_value );
01612     ev.xclient.data.l[ 4 ] = 0;
01613     XSendEvent( display(), window(), False, NoEventMask, &ev );
01614     XSync(display(),false);
01615 #endif
01616 }
01617 
01618 
01619 bool Client::wantsTabFocus() const
01620     {
01621     return ( isNormalWindow() || isDialog()) && wantsInput();
01622     }
01623 
01624 
01625 bool Client::wantsInput() const
01626     {
01627     return rules()->checkAcceptFocus( input || Ptakefocus );
01628     }
01629 
01630 bool Client::isSpecialWindow() const
01631     {
01632     return isDesktop() || isDock() || isSplash() || isTopMenu()
01633         || isToolbar(); // TODO
01634     }
01635 
01640 void Client::updateCursor()
01641     {
01642     Position m = mode;
01643     if( !isResizable() || isShade())
01644         m = PositionCenter;
01645     QCursor c;
01646     switch( m )
01647         {
01648         case PositionTopLeft:
01649         case PositionBottomRight:
01650             c = Qt::SizeFDiagCursor;
01651             break;
01652         case PositionBottomLeft:
01653         case PositionTopRight:
01654             c = Qt::SizeBDiagCursor;
01655             break;
01656         case PositionTop:
01657         case PositionBottom:
01658             c = Qt::SizeVerCursor;
01659             break;
01660         case PositionLeft:
01661         case PositionRight:
01662             c = Qt::SizeHorCursor;
01663             break;
01664         default:
01665             if( moveResizeMode )
01666                 c = Qt::SizeAllCursor;
01667             else
01668                 c = Qt::ArrowCursor;
01669             break;
01670         }
01671     if( c.handle() == cursor.handle())
01672         return;
01673     cursor = c;
01674     if( decoration != NULL )
01675         decoration->widget()->setCursor( cursor );
01676     XDefineCursor( display(), frameId(), cursor.handle());
01677     if( moveResizeMode ) // XDefineCursor doesn't change cursor if there's pointer grab active
01678         XChangeActivePointerGrab( display(),
01679             ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask,
01680             cursor.handle(), xTime());
01681     }
01682 
01683 Client::Position Client::mousePosition( const QPoint& p ) const
01684     {
01685     if( decoration != NULL )
01686         return decoration->mousePosition( p );
01687     return PositionCenter;
01688     }
01689 
01690 void Client::updateAllowedActions( bool force )
01691     {
01692     if( !isManaged() && !force )
01693         return;
01694     unsigned long old_allowed_actions = allowed_actions;
01695     allowed_actions = 0;
01696     if( isMovable())
01697         allowed_actions |= NET::ActionMove;
01698     if( isResizable())
01699         allowed_actions |= NET::ActionResize;
01700     if( isMinimizable())
01701         allowed_actions |= NET::ActionMinimize;
01702     if( isShadeable())
01703         allowed_actions |= NET::ActionShade;
01704     // sticky state not supported
01705     if( isMaximizable())
01706         allowed_actions |= NET::ActionMax;
01707     if( userCanSetFullScreen())
01708         allowed_actions |= NET::ActionFullScreen;
01709     allowed_actions |= NET::ActionChangeDesktop; // always (pagers shouldn't show Docks etc.)
01710     if( isCloseable())
01711         allowed_actions |= NET::ActionClose;
01712     if( old_allowed_actions == allowed_actions )
01713         return;
01714     // TODO this could be delayed and compressed - it's only for pagers etc. anyway
01715     info->setAllowedActions( allowed_actions );
01716     // TODO this should also tell the decoration, so that it can update the buttons
01717     }
01718 
01719 void Client::autoRaise()
01720     {
01721     workspace()->raiseClient( this );
01722     cancelAutoRaise();
01723     }
01724     
01725 void Client::cancelAutoRaise()
01726     {
01727     delete autoRaiseTimer;
01728     autoRaiseTimer = 0;
01729     }
01730 
01731 void Client::debug( kdbgstream& stream ) const
01732     {
01733     stream << "\'ID:" << window() << ";WMCLASS:" << resourceClass() << ":" << resourceName() << ";Caption:" << caption() << "\'";
01734     }
01735 
01736 QPixmap * kwin_get_menu_pix_hack()
01737     {
01738     static QPixmap p;
01739     if ( p.isNull() )
01740         p = SmallIcon( "bx2" );
01741     return &p;
01742     }
01743 
01744 } // namespace
01745 
01746 #include "client.moc"

KWin

Skip menu "KWin"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal