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     getWmClientLeader();
00097     getWmClientMachine();
00098     // first only read the caption text, so that setupWindowRules() can use it for matching,
00099     // and only then really set the caption using setCaption(), which checks for duplicates etc.
00100     // and also relies on rules already existing
00101     cap_normal = readName();
00102     setupWindowRules( false );
00103     setCaption( cap_normal, true );
00104 
00105     detectNoBorder();
00106     fetchIconicName();
00107     getWMHints(); // needs to be done before readTransient() because of reading the group
00108     modal = ( info->state() & NET::Modal ) != 0; // needs to be valid before handling groups
00109     readTransient();
00110     getIcons();
00111     getWindowProtocols();
00112     getWmNormalHints(); // get xSizeHint
00113     getMotifHints();
00114 
00115     // TODO try to obey all state information from info->state()
00116 
00117     original_skip_taskbar = skip_taskbar = ( info->state() & NET::SkipTaskbar) != 0;
00118     skip_pager = ( info->state() & NET::SkipPager) != 0;
00119 
00120     KStartupInfoId asn_id;
00121     KStartupInfoData asn_data;
00122     bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data );
00123 
00124     workspace()->updateClientLayer( this );
00125 
00126     SessionInfo* session = workspace()->takeSessionInfo( this );
00127     
00128     if ( session )
00129         {
00130         if ( session->minimized )
00131             init_minimize = true;
00132         if( session->userNoBorder )
00133             setUserNoBorder( true );
00134         }
00135 
00136     setShortcut( rules()->checkShortcut( session ? session->shortcut : QString::null, true ));
00137 
00138     init_minimize = rules()->checkMinimize( init_minimize, !isMapped );
00139     if( rules()->checkNoBorder( false, !isMapped ))
00140         setUserNoBorder( true );
00141     
00142     checkAndSetInitialRuledOpacity();
00143 
00144     // initial desktop placement
00145     if ( session ) 
00146         {
00147         desk = session->desktop;
00148         if( session->onAllDesktops )
00149             desk = NET::OnAllDesktops;
00150         }
00151     else
00152         {
00153         // if this window is transient, ensure that it is opened on the
00154         // same window as its parent.  this is necessary when an application
00155         // starts up on a different desktop than is currently displayed
00156         if( isTransient())
00157             {
00158             ClientList mainclients = mainClients();
00159             bool on_current = false;
00160             Client* maincl = NULL;
00161             // this is slightly duplicated from Placement::placeOnMainWindow()
00162             for( ClientList::ConstIterator it = mainclients.begin();
00163                  it != mainclients.end();
00164                  ++it )
00165                 {
00166                 if( (*it)->isSpecialWindow() && !(*it)->isOverride())
00167                     continue; // don't consider toolbars etc when placing
00168                 maincl = *it;
00169                 if( (*it)->isOnCurrentDesktop())
00170                     on_current = true;
00171                 }
00172             if( on_current )
00173                 desk = workspace()->currentDesktop();
00174             else if( maincl != NULL )
00175                 desk = maincl->desktop();
00176             }
00177         if ( info->desktop() )
00178             desk = info->desktop(); // window had the initial desktop property, force it
00179         if( desktop() == 0 && asn_valid && asn_data.desktop() != 0 )
00180             desk = asn_data.desktop();
00181         }
00182     if ( desk == 0 ) // assume window wants to be visible on the current desktop
00183         desk = workspace()->currentDesktop();
00184     desk = rules()->checkDesktop( desk, !isMapped );
00185     if( desk != NET::OnAllDesktops ) // do range check
00186         desk = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desk ));
00187     info->setDesktop( desk );
00188     workspace()->updateOnAllDesktopsOfTransients( this ); // SELI
00189 //    onAllDesktopsChange(); decoration doesn't exist here yet
00190 
00191     QRect geom( attr.x, attr.y, attr.width, attr.height );
00192     bool placementDone = FALSE;
00193 
00194     if ( session )
00195         geom = session->geometry;
00196 
00197     QRect area;
00198     bool partial_keep_in_area = isMapped || session;
00199     if( isMapped || session )
00200         area = workspace()->clientArea( FullArea, geom.center(), desktop());
00201     else if( options->xineramaPlacementEnabled )
00202         area = workspace()->clientArea( PlacementArea, QCursor::pos(), desktop());
00203     else
00204         area = workspace()->clientArea( PlacementArea, geom.center(), desktop());
00205 
00206     if( checkFullScreenHack( geom ))
00207         {
00208         fullscreen_mode = FullScreenHack;
00209         geom = workspace()->clientArea( FullScreenArea, geom.center(), desktop());
00210         placementDone = true;
00211         }
00212 
00213     if ( isDesktop() ) 
00214         {
00215         // desktops are treated slightly special
00216         geom = workspace()->clientArea( FullArea, geom.center(), desktop());
00217         placementDone = true;
00218         }
00219 
00220     bool usePosition = false;
00221     if ( isMapped || session || placementDone )
00222         placementDone = true; // use geometry
00223     else if( isTransient() && !isUtility() && !isDialog() && !isSplash())
00224         usePosition = true;
00225     else if( isTransient() && !hasNETSupport())
00226         usePosition = true;
00227     else if( isDialog() && hasNETSupport())
00228     // if the dialog is actually non-NETWM transient window, don't try to apply placement to it,
00229     // it breaks with too many things (xmms, display)
00230         {
00231         if( mainClients().count() >= 1 )
00232             {
00233 #if 1
00234             // TODO #78082 - Ok, it seems there are after all some cases when an application has a good
00235             // reason to specify a position for its dialog. Too bad other WMs have never bothered
00236             // with placement for dialogs, so apps always specify positions for their dialogs,
00237             // including such silly positions like always centered on the screen or under mouse.
00238             // Using ignoring requested position in window-specific settings helps, but at least
00239             // for Qt apps this should work better.
00240             usePosition = true;
00241 #else
00242             ; // force using placement policy
00243 #endif
00244             }
00245         else
00246             usePosition = true;
00247         }
00248     else if( isSplash())
00249         ; // force using placement policy
00250     else
00251         usePosition = true;
00252     if( !rules()->checkIgnorePosition( !usePosition ))
00253         {
00254         bool ignorePPosition = ( options->ignorePositionClasses.contains(QString::fromLatin1(resourceClass())));
00255 
00256         if ( ( (xSizeHint.flags & PPosition) && !ignorePPosition ) ||
00257              (xSizeHint.flags & USPosition) ) 
00258             {
00259             placementDone = TRUE;
00260             // disobey xinerama placement option for now (#70943)
00261             area = workspace()->clientArea( PlacementArea, geom.center(), desktop());
00262             }
00263         }
00264     if( true ) // size is always obeyed for now, only with constraints applied
00265         if ( (xSizeHint.flags & USSize) || (xSizeHint.flags & PSize) ) 
00266             {
00267             // keep in mind that we now actually have a size :-)
00268             }
00269 
00270     if (xSizeHint.flags & PMaxSize)
00271         geom.setSize( geom.size().boundedTo(
00272             rules()->checkMaxSize( QSize(xSizeHint.max_width, xSizeHint.max_height ) ) ) );
00273     if (xSizeHint.flags & PMinSize)
00274         geom.setSize( geom.size().expandedTo(
00275             rules()->checkMinSize( QSize(xSizeHint.min_width, xSizeHint.min_height ) ) ) );
00276 
00277     if( isMovable())
00278         {
00279         if( geom.x() > area.right() || geom.y() > area.bottom())
00280             placementDone = FALSE; // weird, do not trust.
00281         }
00282 
00283     if ( placementDone )
00284         move( geom.x(), geom.y() ); // before gravitating
00285 
00286     updateDecoration( false ); // also gravitates
00287     // TODO is CentralGravity right here, when resizing is done after gravitating?
00288     plainResize( rules()->checkSize( sizeForClientSize( geom.size()), !isMapped ));
00289 
00290     QPoint forced_pos = rules()->checkPosition( invalidPoint, !isMapped );
00291     if( forced_pos != invalidPoint )
00292         {
00293         move( forced_pos );
00294         placementDone = true;
00295         // don't keep inside workarea if the window has specially configured position
00296         partial_keep_in_area = true;
00297         area = workspace()->clientArea( FullArea, geom.center(), desktop());
00298         }
00299     if( !placementDone ) 
00300         { // placement needs to be after setting size
00301         workspace()->place( this, area );
00302         placementDone = TRUE;
00303         }
00304 
00305     if(( !isSpecialWindow() || isToolbar()) && isMovable())
00306         keepInArea( area, partial_keep_in_area );
00307 
00308     XShapeSelectInput( qt_xdisplay(), window(), ShapeNotifyMask );
00309     if ( (is_shape = Shape::hasShape( window())) ) 
00310         {
00311         updateShape();
00312         }
00313 //    else
00314 //  setShapable(FALSE);
00315     
00316     //CT extra check for stupid jdk 1.3.1. But should make sense in general
00317     // if client has initial state set to Iconic and is transient with a parent
00318     // window that is not Iconic, set init_state to Normal
00319     if( init_minimize && isTransient())
00320         {
00321         ClientList mainclients = mainClients();
00322         for( ClientList::ConstIterator it = mainclients.begin();
00323              it != mainclients.end();
00324              ++it )
00325             if( (*it)->isShown( true ))
00326                 init_minimize = false; // SELI even e.g. for NET::Utility?
00327         }
00328 
00329     if( init_minimize )
00330         minimize( true ); // no animation
00331 
00332     // SELI this seems to be mainly for kstart and ksystraycmd
00333     // probably should be replaced by something better
00334     bool doNotShow = false;
00335     if ( workspace()->isNotManaged( caption() ) )
00336         doNotShow = TRUE;
00337 
00338     // other settings from the previous session
00339     if ( session ) 
00340         {
00341         // session restored windows are not considered to be new windows WRT rules,
00342         // i.e. obey only forcing rules
00343         setKeepAbove( session->keepAbove );
00344         setKeepBelow( session->keepBelow );
00345         setSkipTaskbar( session->skipTaskbar, true );
00346         setSkipPager( session->skipPager );
00347         setShade( session->shaded ? ShadeNormal : ShadeNone );
00348         if( session->maximized != MaximizeRestore )
00349             {
00350             maximize( (MaximizeMode) session->maximized );
00351             geom_restore = session->restore;
00352             }
00353         if( session->fullscreen == FullScreenHack )
00354             ; // nothing, this should be already set again above
00355         else if( session->fullscreen != FullScreenNone )
00356             {
00357             setFullScreen( true, false );
00358             geom_fs_restore = session->fsrestore;
00359             }
00360         }
00361     else 
00362         {
00363         geom_restore = geometry(); // remember restore geometry
00364         if ( isMaximizable()
00365              && ( width() >= area.width() || height() >= area.height() ) ) 
00366             {
00367             // window is too large for the screen, maximize in the
00368             // directions necessary
00369             if ( width() >= area.width() && height() >= area.height() ) 
00370                 {
00371                 maximize( Client::MaximizeFull );
00372                 geom_restore = QRect(); // use placement when unmaximizing
00373                 }
00374             else if ( width() >= area.width() ) 
00375                 {
00376                 maximize( Client::MaximizeHorizontal );
00377                 geom_restore = QRect(); // use placement when unmaximizing
00378                 geom_restore.setY( y()); // but only for horizontal direction
00379                 geom_restore.setHeight( height());
00380                 }
00381             else if ( height() >= area.height() ) 
00382                 {
00383                 maximize( Client::MaximizeVertical );
00384                 geom_restore = QRect(); // use placement when unmaximizing
00385                 geom_restore.setX( x()); // but only for vertical direction
00386                 geom_restore.setWidth( width());
00387                 }
00388             }
00389         // window may want to be maximized
00390         // done after checking that the window isn't larger than the workarea, so that
00391         // the restore geometry from the checks above takes precedence, and window
00392         // isn't restored larger than the workarea
00393         MaximizeMode maxmode = static_cast< MaximizeMode >
00394             ((( info->state() & NET::MaxVert ) ? MaximizeVertical : 0 )
00395             | (( info->state() & NET::MaxHoriz ) ? MaximizeHorizontal : 0 ));
00396         MaximizeMode forced_maxmode = rules()->checkMaximize( maxmode, !isMapped );
00397         // either hints were set to maximize, or is forced to maximize,
00398         // or is forced to non-maximize and hints were set to maximize
00399         if( forced_maxmode != MaximizeRestore || maxmode != MaximizeRestore )
00400             maximize( forced_maxmode );
00401 
00402         // read other initial states
00403         setShade( rules()->checkShade( info->state() & NET::Shaded ? ShadeNormal : ShadeNone, !isMapped ));
00404         setKeepAbove( rules()->checkKeepAbove( info->state() & NET::KeepAbove, !isMapped ));
00405         setKeepBelow( rules()->checkKeepBelow( info->state() & NET::KeepBelow, !isMapped ));
00406         setSkipTaskbar( rules()->checkSkipTaskbar( info->state() & NET::SkipTaskbar, !isMapped ), true );
00407         setSkipPager( rules()->checkSkipPager( info->state() & NET::SkipPager, !isMapped ));
00408         if( info->state() & NET::DemandsAttention )
00409             demandAttention();
00410         if( info->state() & NET::Modal )
00411             setModal( true );
00412         if( fullscreen_mode != FullScreenHack && isFullScreenable())
00413             setFullScreen( rules()->checkFullScreen( info->state() & NET::FullScreen, !isMapped ), false );
00414         }
00415 
00416     updateAllowedActions( true );
00417 
00418     // TODO this should avoid flicker, because real restacking is done
00419     // only after manage() finishes, but the window is shown sooner
00420     // - keep it?
00421     XLowerWindow( qt_xdisplay(), frameId());
00422 
00423     user_time = readUserTimeMapTimestamp( asn_valid ? &asn_id : NULL, asn_valid ? &asn_data : NULL, session );
00424 
00425     if( isTopMenu()) // they're shown in Workspace::addClient() if their mainwindow
00426         hideClient( true ); // is the active one
00427 
00428     if ( isShown( true ) && !doNotShow )
00429         {
00430         if( isDialog())
00431             Notify::raise( Notify::TransNew );
00432         if( isNormalWindow())
00433             Notify::raise( Notify::New );
00434 
00435         bool allow;
00436         if( session )
00437             allow = session->active && !workspace()->wasUserInteraction();
00438         else
00439             allow = workspace()->allowClientActivation( this, userTime(), false );
00440 
00441         // if session saving, force showing new windows (i.e. "save file?" dialogs etc.)
00442         // also force if activation is allowed
00443         if( !isOnCurrentDesktop() && !isMapped && !session && ( allow || workspace()->sessionSaving()))
00444             workspace()->setCurrentDesktop( desktop());
00445 
00446         if( isOnCurrentDesktop())
00447             {
00448             setMappingState( NormalState );
00449 
00450             if( isMapped )
00451                 {
00452                 workspace()->raiseClient( this );
00453                 rawShow();
00454                 }
00455             else
00456                 {
00457                 if( allow )
00458                     {
00459                     workspace()->raiseClient( this );
00460                     rawShow();
00461                     if( !isSpecialWindow() || isOverride())
00462                         if ( options->focusPolicyIsReasonable() && wantsTabFocus() )
00463                             workspace()->requestFocus( this );
00464                     }
00465                 else
00466                     {
00467                     workspace()->restackClientUnderActive( this );
00468                     rawShow();
00469                     if( !session && ( !isSpecialWindow() || isOverride()))
00470                         demandAttention();
00471                     }
00472                 }
00473             }
00474         else
00475             {
00476             virtualDesktopChange();
00477             workspace()->raiseClient( this );
00478             if( !session && !isMapped )
00479                 demandAttention();
00480             }
00481         }
00482     else if( !doNotShow ) // !isShown()
00483         {
00484         rawHide();
00485         setMappingState( IconicState );
00486         }
00487     else // doNotShow
00488         { // SELI HACK !!!
00489         hideClient( true );
00490         setMappingState( IconicState );
00491         }
00492     assert( mappingState() != WithdrawnState );
00493 
00494     if( user_time == CurrentTime || user_time == -1U ) // no known user time, set something old
00495         {
00496         user_time = qt_x_time - 1000000;
00497         if( user_time == CurrentTime || user_time == -1U ) // let's be paranoid
00498             user_time = qt_x_time - 1000000 + 10;
00499         }
00500 
00501     updateWorkareaDiffs();
00502 
00503 //    sendSyntheticConfigureNotify(); done when setting mapping state
00504 
00505     delete session;
00506 
00507     ungrabXServer();
00508     
00509     client_rules.discardTemporary();
00510     updateWindowRules(); // was blocked while !isManaged()
00511 
00512 // TODO there's a small problem here - isManaged() depends on the mapping state,
00513 // but this client is not yet in Workspace's client list at this point, will
00514 // be only done in addClient()
00515     return true;
00516     }
00517 
00518 // called only from manage()
00519 void Client::embedClient( Window w, const XWindowAttributes &attr )
00520     {
00521     assert( client == None );
00522     assert( frame == None );
00523     assert( wrapper == None );
00524     client = w;
00525     // we don't want the window to be destroyed when we are destroyed
00526     XAddToSaveSet( qt_xdisplay(), client );
00527     XSelectInput( qt_xdisplay(), client, NoEventMask );
00528     XUnmapWindow( qt_xdisplay(), client );
00529     XWindowChanges wc;     // set the border width to 0
00530     wc.border_width = 0; // TODO possibly save this, and also use it for initial configuring of the window
00531     XConfigureWindow( qt_xdisplay(), client, CWBorderWidth, &wc );
00532 
00533     XSetWindowAttributes swa;
00534     swa.colormap = attr.colormap;
00535     swa.background_pixmap = None;
00536     swa.border_pixel = 0;
00537 
00538     frame = XCreateWindow( qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0,
00539             attr.depth, InputOutput, attr.visual,
00540             CWColormap | CWBackPixmap | CWBorderPixel, &swa );
00541     wrapper = XCreateWindow( qt_xdisplay(), frame, 0, 0, 1, 1, 0,
00542             attr.depth, InputOutput, attr.visual,
00543             CWColormap | CWBackPixmap | CWBorderPixel, &swa );
00544 
00545     XDefineCursor( qt_xdisplay(), frame, arrowCursor.handle());
00546     // some apps are stupid and don't define their own cursor - set the arrow one for them
00547     XDefineCursor( qt_xdisplay(), wrapper, arrowCursor.handle());
00548     XReparentWindow( qt_xdisplay(), client, wrapper, 0, 0 );
00549     XSelectInput( qt_xdisplay(), frame,
00550             KeyPressMask | KeyReleaseMask |
00551             ButtonPressMask | ButtonReleaseMask |
00552             KeymapStateMask |
00553             ButtonMotionMask |
00554             PointerMotionMask |
00555             EnterWindowMask | LeaveWindowMask |
00556             FocusChangeMask |
00557             ExposureMask |
00558             PropertyChangeMask |
00559             StructureNotifyMask | SubstructureRedirectMask |
00560             VisibilityChangeMask );
00561     XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00562     XSelectInput( qt_xdisplay(), client,
00563                   FocusChangeMask |
00564                   PropertyChangeMask |
00565                   ColormapChangeMask |
00566                   EnterWindowMask | LeaveWindowMask |
00567                   KeyPressMask | KeyReleaseMask
00568                   );
00569     updateMouseGrab();
00570     }
00571 
00572 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Sep 30 18:40:43 2005 by doxygen 1.4.3 written by Dimitri van Heesch, © 1997-2003