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