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