kwin Library API Documentation

manage.cpp

00001 /*****************************************************************
00002  KWin - the KDE window manager
00003  This file is part of the KDE project.
00004 
00005 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
00006 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
00007 
00008 You can Freely distribute this program under the GNU General Public
00009 License. See the file "COPYING" for the exact licensing terms.
00010 ******************************************************************/
00011 
00012 /*
00013 
00014  This file contains things relevant to handling incoming events.
00015 
00016 */
00017 
00018 #include "client.h"
00019 
00020 #include <kstartupinfo.h>
00021 #include <kglobal.h>
00022 #include <X11/extensions/shape.h>
00023 
00024 #include "notifications.h"
00025 #include "rules.h"
00026 
00027 extern Time qt_x_time;
00028 
00029 namespace KWinInternal
00030 {
00031 
00037 bool Client::manage( Window w, bool isMapped )
00038     {
00039     XWindowAttributes attr;
00040     if( !XGetWindowAttributes(qt_xdisplay(), w, &attr))
00041         return false;
00042 
00043     grabXServer();
00044 
00045     // from this place on, manage() mustn't return false
00046     block_geometry = 1;
00047 
00048     embedClient( w, attr );
00049 
00050     // SELI order all these things in some sane manner
00051 
00052     bool init_minimize = false;
00053     XWMHints * hints = XGetWMHints(qt_xdisplay(), w );
00054     if (hints && (hints->flags & StateHint) && hints->initial_state == IconicState)
00055         init_minimize = true;
00056     if (hints)
00057         XFree(hints);
00058     if( isMapped )
00059         init_minimize = false; // if it's already mapped, ignore hint
00060 
00061     unsigned long properties[ 2 ];
00062     properties[ WinInfo::PROTOCOLS ] =
00063         NET::WMDesktop |
00064         NET::WMState |
00065         NET::WMWindowType |
00066         NET::WMStrut |
00067         NET::WMName |
00068         NET::WMIconGeometry |
00069         NET::WMIcon |
00070         NET::WMPid |
00071         NET::WMIconName |
00072         0;
00073     properties[ WinInfo::PROTOCOLS2 ] =
00074         NET::WM2UserTime |
00075         NET::WM2StartupId |
00076         NET::WM2ExtendedStrut |
00077         0;
00078 
00079     info = new WinInfo( this, qt_xdisplay(), client, qt_xrootwin(), properties, 2 );
00080 
00081     cmap = attr.colormap;
00082 
00083     XClassHint classHint;
00084     if ( XGetClassHint( qt_xdisplay(), client, &classHint ) ) 
00085         {
00086         // Qt3.2 and older had this all lowercase, Qt3.3 capitalized resource class
00087         // force lowercase, so that workarounds listing resource classes still work
00088         resource_name = QCString( classHint.res_name ).lower();
00089         resource_class = QCString( classHint.res_class ).lower();
00090         XFree( classHint.res_name );
00091         XFree( classHint.res_class );
00092         }
00093     ignore_focus_stealing = options->checkIgnoreFocusStealing( this ); // TODO change to rules
00094 
00095     window_role = staticWindowRole( w );
00096     // first only read the caption text, so that setupWindowRules() can use it for matching,
00097     // and only then really set the caption using setCaption(), which checks for duplicates etc.
00098     // and also relies on rules already existing
00099     cap_normal = readName();
00100     setupWindowRules( false );
00101     setCaption( cap_normal, true );
00102 
00103     detectNoBorder();
00104     fetchIconicName();
00105     getWMHints(); // needs to be done before readTransient() because of reading the group
00106     getWmClientLeader(); // needs to be done before readTransient() because of same app comparing
00107     modal = ( info->state() & NET::Modal ) != 0; // needs to be valid before handling groups
00108     readTransient();
00109     getIcons();
00110     getWindowProtocols();
00111     getWmNormalHints(); // get xSizeHint
00112     getMotifHints();
00113 
00114     // TODO try to obey all state information from info->state()
00115 
00116     original_skip_taskbar = skip_taskbar = ( info->state() & NET::SkipTaskbar) != 0;
00117     skip_pager = ( info->state() & NET::SkipPager) != 0;
00118 
00119     KStartupInfoId asn_id;
00120     KStartupInfoData asn_data;
00121     bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data );
00122 
00123     workspace()->updateClientLayer( this );
00124 
00125     SessionInfo* session = workspace()->takeSessionInfo( this );
00126     
00127     if ( session )
00128         {
00129         if ( session->minimized )
00130             init_minimize = true;
00131         if( session->userNoBorder )
00132             setUserNoBorder( true );
00133         }
00134 
00135     init_minimize = rules()->checkMinimize( init_minimize, !isMapped );
00136     if( rules()->checkNoBorder( false, !isMapped ))
00137         setUserNoBorder( true );
00138 
00139     // initial desktop placement
00140     if ( info->desktop() )
00141         desk = info->desktop(); // window had the initial desktop property!
00142     else if( asn_valid && asn_data.desktop() != 0 )
00143         desk = asn_data.desktop();
00144     if ( session ) 
00145         {
00146         desk = session->desktop;
00147         if( session->onAllDesktops )
00148             desk = NET::OnAllDesktops;
00149         }
00150     else if ( desk == 0 ) 
00151         {
00152         // if this window is transient, ensure that it is opened on the
00153         // same window as its parent.  this is necessary when an application
00154         // starts up on a different desktop than is currently displayed
00155         if( isTransient())
00156             {
00157             ClientList mainclients = mainClients();
00158             bool on_current = false;
00159             Client* maincl = NULL;
00160             // this is slightly duplicated from Placement::placeOnMainWindow()
00161             for( ClientList::ConstIterator it = mainclients.begin();
00162                  it != mainclients.end();
00163                  ++it )
00164                 {
00165                 if( (*it)->isSpecialWindow() && !(*it)->isOverride())
00166                     continue; // don't consider toolbars etc when placing
00167                 maincl = *it;
00168                 if( (*it)->isOnCurrentDesktop())
00169                     on_current = true;
00170                 }
00171             if( on_current )
00172                 desk = workspace()->currentDesktop();
00173             else if( maincl != NULL )
00174                 desk = maincl->desktop();
00175             }
00176         }
00177     if ( desk == 0 ) // assume window wants to be visible on the current desktop
00178         desk = workspace()->currentDesktop();
00179     desk = rules()->checkDesktop( desk, !isMapped );
00180     if( desk != NET::OnAllDesktops ) // do range check
00181         desk = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desk ));
00182     info->setDesktop( desk );
00183     workspace()->updateOnAllDesktopsOfTransients( this ); // SELI
00184 //    onAllDesktopsChange(); decoration doesn't exist here yet
00185 
00186     QRect geom( attr.x, attr.y, attr.width, attr.height );
00187     bool placementDone = FALSE;
00188 
00189     if ( session )
00190         geom = session->geometry;
00191 
00192     QRect area;
00193     if( isMapped || session )
00194         area = workspace()->clientArea( FullArea, geom.center(), desktop());
00195     else if( options->xineramaPlacementEnabled )
00196         area = workspace()->clientArea( PlacementArea, QCursor::pos(), desktop());
00197     else
00198         area = workspace()->clientArea( PlacementArea, geom.center(), desktop());
00199 
00200     if( checkFullScreenHack( geom ))
00201         {
00202         fullscreen_mode = FullScreenHack;
00203         geom = workspace()->clientArea( FullScreenArea, geom.center(), desktop());
00204         placementDone = true;
00205         }
00206 
00207     if ( isDesktop() ) 
00208         {
00209         // desktops are treated slightly special
00210         geom = workspace()->clientArea( FullArea, geom.center(), desktop());
00211         placementDone = true;
00212         }
00213 
00214     bool usePosition = false;
00215     if ( isMapped || session || placementDone )
00216         placementDone = true; // use geometry
00217     else if( isTransient() && !isUtility() && !isDialog() && !isSplash())
00218         usePosition = true;
00219     else if( isTransient() && !hasNETSupport())
00220         usePosition = true;
00221     else if( isDialog() && hasNETSupport())
00222     // if the dialog is actually non-NETWM transient window, don't try to apply placement to it,
00223     // it breaks with too many things (xmms, display)
00224         {
00225         if( mainClients().count() >= 1 )
00226             ; // force using placement policy
00227         else
00228             usePosition = true;
00229         }
00230     else if( isSplash())
00231         ; // force using placement policy
00232     else
00233         usePosition = true;
00234     if( !rules()->checkIgnorePosition( !usePosition ))
00235         {
00236         bool ignorePPosition = ( options->ignorePositionClasses.contains(QString::fromLatin1(resourceClass())));
00237 
00238         if ( ( (xSizeHint.flags & PPosition) && !ignorePPosition ) ||
00239              (xSizeHint.flags & USPosition) ) 
00240             {
00241             placementDone = TRUE;
00242             // disobey xinerama placement option for now (#70943)
00243             area = workspace()->clientArea( PlacementArea, geom.center(), desktop());
00244             }
00245         }
00246     if( true ) // size is always obeyed for now, only with constraints applied
00247         if ( (xSizeHint.flags & USSize) || (xSizeHint.flags & PSize) ) 
00248             {
00249             // keep in mind that we now actually have a size :-)
00250             }
00251 
00252     if (xSizeHint.flags & PMaxSize)
00253         geom.setSize( geom.size().boundedTo(
00254             rules()->checkMaxSize( QSize(xSizeHint.max_width, xSizeHint.max_height ) ) ) );
00255     if (xSizeHint.flags & PMinSize)
00256         geom.setSize( geom.size().expandedTo(
00257             rules()->checkMinSize( QSize(xSizeHint.min_width, xSizeHint.min_height ) ) ) );
00258 
00259     if( isMovable())
00260         {
00261         if( geom.x() > area.right() || geom.y() > area.bottom())
00262             placementDone = FALSE; // weird, do not trust.
00263         }
00264 
00265     if ( placementDone )
00266         move( geom.x(), geom.y() ); // before gravitating
00267 
00268     updateDecoration( false ); // also gravitates
00269     // TODO is CentralGravity right here, when resizing is done after gravitating?
00270     plainResize( rules()->checkSize( sizeForClientSize( geom.size()), !isMapped ));
00271 
00272     QPoint forced_pos = rules()->checkPosition( invalidPoint, !isMapped );
00273     if( forced_pos != invalidPoint )
00274         {
00275         move( forced_pos );
00276         placementDone = true;
00277         }
00278     if( !placementDone ) 
00279         { // placement needs to be after setting size
00280         workspace()->place( this, area );
00281         placementDone = TRUE;
00282         }
00283 
00284     if( !isMapped && !session // trust position from session or if already mapped
00285         && ( !isSpecialWindow() || isToolbar()) && isMovable())
00286         keepInArea( area );
00287 
00288     XShapeSelectInput( qt_xdisplay(), window(), ShapeNotifyMask );
00289     if ( (is_shape = Shape::hasShape( window())) ) 
00290         {
00291         updateShape();
00292         }
00293 
00294     //CT extra check for stupid jdk 1.3.1. But should make sense in general
00295     // if client has initial state set to Iconic and is transient with a parent
00296     // window that is not Iconic, set init_state to Normal
00297     if( init_minimize && isTransient())
00298         {
00299         ClientList mainclients = mainClients();
00300         for( ClientList::ConstIterator it = mainclients.begin();
00301              it != mainclients.end();
00302              ++it )
00303             if( (*it)->isShown( true ))
00304                 init_minimize = false; // SELI even e.g. for NET::Utility?
00305         }
00306 
00307     if( init_minimize )
00308         minimize( true ); // no animation
00309 
00310     // SELI this seems to be mainly for kstart and ksystraycmd
00311     // probably should be replaced by something better
00312     bool doNotShow = false;
00313     if ( workspace()->isNotManaged( caption() ) )
00314         doNotShow = TRUE;
00315 
00316     // other settings from the previous session
00317     if ( session ) 
00318         {
00319         // session restored windows are not considered to be new windows WRT rules,
00320         // i.e. obey only forcing rules
00321         setKeepAbove( session->keepAbove );
00322         setKeepBelow( session->keepBelow );
00323         setSkipTaskbar( session->skipTaskbar, true );
00324         setSkipPager( session->skipPager );
00325         setShade( session->shaded ? ShadeNormal : ShadeNone );
00326         if( session->maximized != MaximizeRestore )
00327             {
00328             maximize( (MaximizeMode) session->maximized );
00329             geom_restore = session->restore;
00330             }
00331         if( session->fullscreen == FullScreenHack )
00332             ; // nothing, this should be already set again above
00333         else if( session->fullscreen != FullScreenNone )
00334             {
00335             setFullScreen( true, false );
00336             geom_fs_restore = session->fsrestore;
00337             }
00338         }
00339     else 
00340         {
00341         geom_restore = geometry(); // remember restore geometry
00342         if ( isMaximizable()
00343              && ( width() >= area.width() || height() >= area.height() ) ) 
00344             {
00345             // window is too large for the screen, maximize in the
00346             // directions necessary
00347             if ( width() >= area.width() && height() >= area.height() ) 
00348                 {
00349                 maximize( Client::MaximizeFull );
00350                 geom_restore = QRect(); // use placement when unmaximizing
00351                 }
00352             else if ( width() >= area.width() ) 
00353                 {
00354                 maximize( Client::MaximizeHorizontal );
00355                 geom_restore = QRect(); // use placement when unmaximizing
00356                 geom_restore.setY( y()); // but only for horizontal direction
00357                 geom_restore.setHeight( height());
00358                 }
00359             else if ( height() >= area.height() ) 
00360                 {
00361                 maximize( Client::MaximizeVertical );
00362                 geom_restore = QRect(); // use placement when unmaximizing
00363                 geom_restore.setX( x()); // but only for vertical direction
00364                 geom_restore.setWidth( width());
00365                 }
00366             }
00367         // window may want to be maximized
00368         // done after checking that the window isn't larger than the workarea, so that
00369         // the restore geometry from the checks above takes precedence, and window
00370         // isn't restored larger than the workarea
00371         MaximizeMode maxmode = static_cast< MaximizeMode >
00372             ((( info->state() & NET::MaxVert ) ? MaximizeVertical : 0 )
00373             | (( info->state() & NET::MaxHoriz ) ? MaximizeHorizontal : 0 ));
00374         MaximizeMode forced_maxmode = rules()->checkMaximize( maxmode, !isMapped );
00375         // either hints were set to maximize, or is forced to maximize,
00376         // or is forced to non-maximize and hints were set to maximize
00377         if( forced_maxmode != MaximizeRestore || maxmode != MaximizeRestore )
00378             maximize( forced_maxmode );
00379 
00380         // read other initial states
00381         setShade( rules()->checkShade( info->state() & NET::Shaded ? ShadeNormal : ShadeNone, !isMapped ));
00382         setKeepAbove( rules()->checkKeepAbove( info->state() & NET::KeepAbove, !isMapped ));
00383         setKeepBelow( rules()->checkKeepBelow( info->state() & NET::KeepBelow, !isMapped ));
00384         setSkipTaskbar( rules()->checkSkipTaskbar( info->state() & NET::SkipTaskbar, !isMapped ), true );
00385         setSkipPager( rules()->checkSkipPager( info->state() & NET::SkipPager, !isMapped ));
00386         if( info->state() & NET::DemandsAttention )
00387             demandAttention();
00388         if( info->state() & NET::Modal )
00389             setModal( true );
00390         if( fullscreen_mode != FullScreenHack && isFullScreenable())
00391             setFullScreen( rules()->checkFullScreen( info->state() & NET::FullScreen, !isMapped ), false );
00392         }
00393 
00394     updateAllowedActions( true );
00395 
00396     // TODO this should avoid flicker, because real restacking is done
00397     // only after manage() finishes, but the window is shown sooner
00398     // - keep it?
00399     XLowerWindow( qt_xdisplay(), frameId());
00400 
00401     user_time = readUserTimeMapTimestamp( asn_valid ? &asn_id : NULL, asn_valid ? &asn_data : NULL, session );
00402 
00403     if( isTopMenu()) // they're shown in Workspace::addClient() if their mainwindow
00404         hideClient( true ); // is the active one
00405 
00406     if ( isShown( true ) && !doNotShow )
00407         {
00408         if( isDialog())
00409             Notify::raise( Notify::TransNew );
00410         if( isNormalWindow())
00411             Notify::raise( Notify::New );
00412 
00413         bool allow;
00414         if( session )
00415             allow = session->active && !workspace()->wasUserInteraction();
00416         else
00417             allow = workspace()->allowClientActivation( this, userTime(), false );
00418 
00419         // if session saving, force showing new windows (i.e. "save file?" dialogs etc.)
00420         // also force if activation is allowed
00421         if( !isOnCurrentDesktop() && !isMapped && !session && ( allow || workspace()->sessionSaving()))
00422             workspace()->setCurrentDesktop( desktop());
00423 
00424         if( isOnCurrentDesktop())
00425             {
00426             setMappingState( NormalState );
00427 
00428             if( isMapped )
00429                 {
00430                 workspace()->raiseClient( this );
00431                 rawShow();
00432                 }
00433             else
00434                 {
00435                 if( allow )
00436                     {
00437                     workspace()->raiseClient( this );
00438                     rawShow();
00439                     if( !isSpecialWindow() || isOverride())
00440                         if ( options->focusPolicyIsReasonable() && wantsTabFocus() )
00441                             workspace()->requestFocus( this );
00442                     }
00443                 else
00444                     {
00445                     workspace()->restackClientUnderActive( this );
00446                     rawShow();
00447                     if( !session && ( !isSpecialWindow() || isOverride()))
00448                         demandAttention();
00449                     }
00450                 }
00451             }
00452         else
00453             {
00454             virtualDesktopChange();
00455             workspace()->raiseClient( this );
00456             if( !session && !isMapped )
00457                 demandAttention();
00458             }
00459         }
00460     else if( !doNotShow ) // !isShown()
00461         {
00462         rawHide();
00463         setMappingState( IconicState );
00464         }
00465     else // doNotShow
00466         { // SELI HACK !!!
00467         hideClient( true );
00468         setMappingState( IconicState );
00469         }
00470     assert( mappingState() != WithdrawnState );
00471 
00472     if( user_time == CurrentTime || user_time == -1U ) // no known user time, set something old
00473         {
00474         user_time = qt_x_time - 1000000;
00475         if( user_time == CurrentTime || user_time == -1U ) // let's be paranoid
00476             user_time = qt_x_time - 1000000 + 10;
00477         }
00478 
00479     updateWorkareaDiffs();
00480 
00481 //    sendSyntheticConfigureNotify(); done when setting mapping state
00482 
00483     delete session;
00484 
00485     checkActiveModal();
00486 
00487     ungrabXServer();
00488     
00489     client_rules.discardTemporary();
00490     updateWindowRules(); // was blocked while !isManaged()
00491 
00492     return true;
00493     }
00494 
00495 // called only from manage()
00496 void Client::embedClient( Window w, const XWindowAttributes &attr )
00497     {
00498     assert( client == None );
00499     assert( frame == None );
00500     assert( wrapper == None );
00501     client = w;
00502     // we don't want the window to be destroyed when we are destroyed
00503     XAddToSaveSet( qt_xdisplay(), client );
00504     XSelectInput( qt_xdisplay(), client, NoEventMask );
00505     XUnmapWindow( qt_xdisplay(), client );
00506     XWindowChanges wc;     // set the border width to 0
00507     wc.border_width = 0; // TODO possibly save this, and also use it for initial configuring of the window
00508     XConfigureWindow( qt_xdisplay(), client, CWBorderWidth, &wc );
00509 
00510     XSetWindowAttributes swa;
00511     swa.colormap = attr.colormap;
00512     swa.background_pixmap = None;
00513     swa.border_pixel = 0;
00514 
00515     frame = XCreateWindow( qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0,
00516             attr.depth, InputOutput, attr.visual,
00517             CWColormap | CWBackPixmap | CWBorderPixel, &swa );
00518     wrapper = XCreateWindow( qt_xdisplay(), frame, 0, 0, 1, 1, 0,
00519             attr.depth, InputOutput, attr.visual,
00520             CWColormap | CWBackPixmap | CWBorderPixel, &swa );
00521 
00522     XDefineCursor( qt_xdisplay(), frame, arrowCursor.handle());
00523     // some apps are stupid and don't define their own cursor - set the arrow one for them
00524     XDefineCursor( qt_xdisplay(), wrapper, arrowCursor.handle());
00525     XReparentWindow( qt_xdisplay(), client, wrapper, 0, 0 );
00526     XSelectInput( qt_xdisplay(), frame,
00527             KeyPressMask | KeyReleaseMask |
00528             ButtonPressMask | ButtonReleaseMask |
00529             KeymapStateMask |
00530             ButtonMotionMask |
00531             PointerMotionMask |
00532             EnterWindowMask | LeaveWindowMask |
00533             FocusChangeMask |
00534             ExposureMask |
00535             PropertyChangeMask |
00536             StructureNotifyMask | SubstructureRedirectMask |
00537             VisibilityChangeMask );
00538     XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00539     XSelectInput( qt_xdisplay(), client,
00540                   FocusChangeMask |
00541                   PropertyChangeMask |
00542                   ColormapChangeMask |
00543                   EnterWindowMask | LeaveWindowMask |
00544                   KeyPressMask | KeyReleaseMask
00545                   );
00546     updateMouseGrab();
00547     }
00548 
00549 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Aug 20 13:39:13 2006 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003