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 
00026 extern Time qt_x_time;
00027 extern Atom qt_window_role;
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         0;
00077 
00078     info = new WinInfo( this, qt_xdisplay(), client, qt_xrootwin(), properties, 2 );
00079 
00080     cmap = attr.colormap;
00081 
00082     bool mresize, mmove, mminimize, mmaximize, mclose;
00083     if( Motif::funcFlags( client, mresize, mmove, mminimize, mmaximize, mclose )) 
00084         {
00085         if( !hasNETSupport()) // NETWM apps should set type and size constraints
00086             {
00087             motif_may_resize = mresize; // this should be set using minsize==maxsize, but oh well
00088             motif_may_move = mmove;
00089             }
00090         // mminimize; - ignore, bogus - e.g. shading or sending to another desktop is "minimizing" too
00091         // mmaximize; - ignore, bogus - maximizing is basically just resizing
00092         motif_may_close = mclose; // motif apps like to crash when they set this hint and WM closes them anyway
00093         }
00094 
00095     XClassHint classHint;
00096     if ( XGetClassHint( qt_xdisplay(), client, &classHint ) ) 
00097         {
00098         // Qt3.2 and older had this all lowercase, Qt3.3 capitalized resource class
00099         // force lowercase, so that workarounds listing resource classes still work
00100         resource_name = QCString( classHint.res_name ).lower();
00101         resource_class = QCString( classHint.res_class ).lower();
00102         XFree( classHint.res_name );
00103         XFree( classHint.res_class );
00104         }
00105     ignore_focus_stealing = options->checkIgnoreFocusStealing( this );
00106 
00107     detectNoBorder();
00108     fetchName();
00109     fetchIconicName();
00110     getWMHints(); // needs to be done before readTransient() because of reading the group
00111     getWmClientLeader(); // needs to be done before readTransient() because of same app comparing
00112     readTransient();
00113     getIcons();
00114     getWindowProtocols();
00115     getWmNormalHints(); // get xSizeHint
00116     window_role = getStringProperty( w, qt_window_role );
00117 
00118     // TODO try to obey all state information from info->state()
00119 
00120     original_skip_taskbar = skip_taskbar = ( info->state() & NET::SkipTaskbar) != 0;
00121     skip_pager = ( info->state() & NET::SkipPager) != 0;
00122     modal = ( info->state() & NET::Modal ) != 0;
00123 
00124     // window wants to stay on top?
00125     keep_above = ( info->state() & NET::KeepAbove ) != 0;
00126     // window wants to stay on bottom?
00127     keep_below = ( info->state() & NET::KeepBelow ) != 0;
00128     if( keep_above && keep_below )
00129         keep_above = keep_below = false;
00130 
00131     KStartupInfoData asn_data;
00132     bool asn_valid = workspace()->checkStartupNotification( window(), asn_data );
00133 
00134     workspace()->updateClientLayer( this );
00135 
00136     SessionInfo* session = workspace()->takeSessionInfo( this );
00137     if ( session )
00138         {
00139         if ( session->minimized )
00140             init_minimize = true;
00141         if( session->userNoBorder )
00142             setUserNoBorder( true );
00143         }
00144 
00145     // initial desktop placement
00146     if ( info->desktop() )
00147         desk = info->desktop(); // window had the initial desktop property!
00148     else if( asn_valid && asn_data.desktop() != 0 )
00149         desk = asn_data.desktop();
00150     if ( session ) 
00151         {
00152         desk = session->desktop;
00153         if( session->onAllDesktops )
00154             desk = NET::OnAllDesktops;
00155         }
00156     else if ( desk == 0 ) 
00157         {
00158         // if this window is transient, ensure that it is opened on the
00159         // same window as its parent.  this is necessary when an application
00160         // starts up on a different desktop than is currently displayed
00161         if( isTransient())
00162             {
00163             ClientList mainclients = mainClients();
00164             bool on_current = false;
00165             Client* maincl = NULL;
00166             // this is slightly duplicated from Placement::placeOnMainWindow()
00167             for( ClientList::ConstIterator it = mainclients.begin();
00168                  it != mainclients.end();
00169                  ++it )
00170                 {
00171                 if( (*it)->isSpecialWindow() && !(*it)->isOverride())
00172                     continue; // don't consider toolbars etc when placing
00173                 maincl = *it;
00174                 if( (*it)->isOnCurrentDesktop())
00175                     on_current = true;
00176                 }
00177             if( on_current )
00178                 desk = workspace()->currentDesktop();
00179             else if( maincl != NULL )
00180                 desk = maincl->desktop();
00181             }
00182         }
00183     if ( desk == 0 ) // assume window wants to be visible on the current desktop
00184         desk = workspace()->currentDesktop();
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     if( isMapped || session )
00199         area = workspace()->clientArea( WorkArea, geom.center(), desktop());
00200     else if( options->xineramaPlacementEnabled )
00201         area = workspace()->clientArea( PlacementArea, QCursor::pos(), desktop());
00202     else
00203         area = workspace()->clientArea( PlacementArea, geom.center(), desktop());
00204 
00205     // if it's noborder window, and has size of one screen or the whole desktop geometry, it's fullscreen hack
00206     if( ( geom.size() == workspace()->clientArea( FullArea, geom.center(), desktop()).size()
00207             || geom.size() == workspace()->clientArea( ScreenArea, geom.center(), desktop()).size())
00208         && noBorder() && !isUserNoBorder() && isFullScreenable( true )) 
00209         {
00210         fullscreen_mode = FullScreenHack;
00211         geom = workspace()->clientArea( MaximizeFullArea, geom.center(), desktop());
00212         placementDone = true;
00213         }
00214 
00215     if ( isDesktop() ) 
00216         {
00217         // desktops are treated slightly special
00218         geom = workspace()->clientArea( FullArea, geom.center(), desktop());
00219         placementDone = true;
00220         }
00221 
00222     if ( isMapped || session || placementDone
00223          || ( isTransient() && !isUtility() && !isDialog() && !isSplash())) 
00224         {  // TODO
00225         placementDone = TRUE;
00226         }
00227     else if( isTransient() && !hasNETSupport())
00228         placementDone = true;
00229     else if( isDialog() && hasNETSupport()) // see Placement::placeDialog()
00230         ; // force using placement policy
00231     else if( isSplash())
00232         ; // force using placement policy
00233     else
00234         {
00235         bool ignorePPosition = ( options->ignorePositionClasses.contains(QString::fromLatin1(resourceClass())));
00236 
00237         if ((xSizeHint.flags & PPosition) && ! ignorePPosition) 
00238             {
00239             int tx = geom.x();
00240             int ty = geom.y();
00241 
00242 // TODO tyhle testy nepocitaji s dekoraci, ani s gravity
00243             if (tx < 0)
00244                 tx = area.right() + tx;
00245             if (ty < 0)
00246                 ty = area.bottom() + ty;
00247             geom.moveTopLeft(QPoint(tx, ty));
00248             }
00249 
00250         if ( ( (xSizeHint.flags & PPosition) && !ignorePPosition ) ||
00251              (xSizeHint.flags & USPosition) ) 
00252             {
00253             placementDone = TRUE;
00254             // disobey xinerama placement option for now (#70943)
00255             area = workspace()->clientArea( PlacementArea, geom.center(), desktop());
00256             }
00257         if ( (xSizeHint.flags & USSize) || (xSizeHint.flags & PSize) ) 
00258             {
00259             // keep in mind that we now actually have a size :-)
00260             }
00261         }
00262 
00263     if (xSizeHint.flags & PMaxSize)
00264         geom.setSize( geom.size().boundedTo( QSize(xSizeHint.max_width, xSizeHint.max_height ) ) );
00265     if (xSizeHint.flags & PMinSize)
00266         geom.setSize( geom.size().expandedTo( QSize(xSizeHint.min_width, xSizeHint.min_height ) ) );
00267 
00268     if( isMovable())
00269         {
00270         if( geom.x() > area.right() || geom.y() > area.bottom())
00271             placementDone = FALSE; // weird, do not trust.
00272         }
00273 
00274     if ( placementDone )
00275         move( geom.x(), geom.y() ); // before gravitating
00276 
00277     updateDecoration( false ); // also gravitates
00278     // TODO is CentralGravity right here, when resizing is done after gravitating?
00279     plainResize( sizeForClientSize( geom.size()));
00280 
00281     if( !placementDone ) 
00282         { // placement needs to be after setting size
00283         workspace()->place( this, area );
00284         placementDone = TRUE;
00285         }
00286 
00287     if( !isMapped && !session // trust position from session or if already mapped
00288         && ( !isSpecialWindow() || isToolbar()) && isMovable())
00289         keepInArea( area );
00290 
00291     XShapeSelectInput( qt_xdisplay(), window(), ShapeNotifyMask );
00292     if ( (is_shape = Shape::hasShape( window())) ) 
00293         {
00294         updateShape();
00295         }
00296 
00297     //CT extra check for stupid jdk 1.3.1. But should make sense in general
00298     // if client has initial state set to Iconic and is transient with a parent
00299     // window that is not Iconic, set init_state to Normal
00300     if( init_minimize && isTransient())
00301         {
00302         ClientList mainclients = mainClients();
00303         for( ClientList::ConstIterator it = mainclients.begin();
00304              it != mainclients.end();
00305              ++it )
00306             if( (*it)->isShown( true ))
00307                 init_minimize = false; // SELI even e.g. for NET::Utility?
00308         }
00309 
00310     if( init_minimize )
00311         minimize();
00312 
00313     // SELI this seems to be mainly for kstart and ksystraycmd
00314     // probably should be replaced by something better
00315     bool doNotShow = false;
00316     if ( workspace()->isNotManaged( caption() ) )
00317         doNotShow = TRUE;
00318 
00319     // other settings from the previous session
00320     if ( session ) 
00321         {
00322         setKeepAbove( session->keepAbove );
00323         setKeepBelow( session->keepBelow );
00324         setSkipTaskbar( session->skipTaskbar, true );
00325         setSkipPager( session->skipPager );
00326         setShade( session->shaded ? ShadeNormal : ShadeNone );
00327         if( session->maximized != MaximizeRestore )
00328             {
00329             maximize( (MaximizeMode) session->maximized );
00330             geom_restore = session->restore;
00331             }
00332         if( session->fullscreen == FullScreenHack )
00333             ; // nothing, this should be already set again above
00334         else if( session->fullscreen != FullScreenNone )
00335             {
00336             setFullScreen( true, false );
00337             geom_fs_restore = session->fsrestore;
00338             }
00339         }
00340     else 
00341         {
00342         geom_restore = geometry(); // remember restore geometry
00343         if ( isMaximizable()
00344              && ( width() >= area.width() || height() >= area.height() ) ) 
00345             {
00346             // window is too large for the screen, maximize in the
00347             // directions necessary
00348             if ( width() >= area.width() && height() >= area.height() ) 
00349                 {
00350                 maximize( Client::MaximizeFull );
00351                 geom_restore = QRect(); // use placement when unmaximizing
00352                 }
00353             else if ( width() >= area.width() ) 
00354                 {
00355                 maximize( Client::MaximizeHorizontal );
00356                 geom_restore = QRect(); // use placement when unmaximizing
00357                 geom_restore.setY( y()); // but only for horizontal direction
00358                 geom_restore.setHeight( height());
00359                 }
00360             else if ( height() >= area.height() ) 
00361                 {
00362                 maximize( Client::MaximizeVertical );
00363                 geom_restore = QRect(); // use placement when unmaximizing
00364                 geom_restore.setX( x()); // but only for vertical direction
00365                 geom_restore.setWidth( width());
00366                 }
00367             }
00368         // window may want to be maximized
00369         // done after checking that the window isn't larger than the workarea, so that
00370         // the restore geometry from the checks above takes precedence, and window
00371         // isn't restored larger than the workarea
00372         if ( (info->state() & NET::Max) == NET::Max )
00373             maximize( Client::MaximizeFull );
00374         else if ( info->state() & NET::MaxVert )
00375             maximize( Client::MaximizeVertical );
00376         else if ( info->state() & NET::MaxHoriz )
00377             maximize( Client::MaximizeHorizontal );
00378 
00379         // read other initial states
00380         if( info->state() & NET::Shaded )
00381             setShade( ShadeNormal );
00382         if( info->state() & NET::KeepAbove )
00383             setKeepAbove( true );
00384         if( info->state() & NET::KeepBelow )
00385             setKeepBelow( true );
00386         if( info->state() & NET::SkipTaskbar )
00387             setSkipTaskbar( true, true );
00388         if( info->state() & NET::SkipPager )
00389             setSkipPager( true );
00390         if( info->state() & NET::DemandsAttention )
00391             demandAttention();
00392         if( info->state() & NET::Modal )
00393             setModal( true );
00394         if( fullscreen_mode != FullScreenHack )
00395             {
00396             if(( info->state() & NET::FullScreen ) != 0 && isFullScreenable())
00397                 setFullScreen( true, false );
00398             }
00399         }
00400 
00401     updateAllowedActions( true );
00402 
00403     // TODO this should avoid flicker, because real restacking is done
00404     // only after manage() finishes, but the window is shown sooner
00405     // - keep it?
00406     XLowerWindow( qt_xdisplay(), frameId());
00407 
00408     user_time = readUserTimeMapTimestamp( asn_valid ? &asn_data : NULL, session );
00409 
00410     if( isTopMenu()) // they're shown in Workspace::addClient() if their mainwindow
00411         hideClient( true ); // is the active one
00412 
00413     if ( isShown( true ) && !doNotShow )
00414         {
00415         if( isDialog())
00416             Notify::raise( Notify::TransNew );
00417         if( isNormalWindow())
00418             Notify::raise( Notify::New );
00419 
00420         // if session saving, force showing new windows (i.e. "save file?" dialogs etc.)
00421         if( workspace()->sessionSaving() && !isOnCurrentDesktop())
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( workspace()->allowClientActivation( this, userTime(), false, session && session->active ))
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 || session->fake ) && ( !isSpecialWindow() || isOverride()))
00448                         demandAttention();
00449                     }
00450                 }
00451             }
00452         else
00453             {
00454             virtualDesktopChange();
00455             workspace()->raiseClient( this );
00456             if( ( !session || session->fake ) && !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     ungrabXServer();
00486 
00487     return true;
00488     }
00489 
00490 // called only from manage()
00491 void Client::embedClient( Window w, const XWindowAttributes &attr )
00492     {
00493     assert( client == None );
00494     assert( frame == None );
00495     assert( wrapper == None );
00496     client = w;
00497     // we don't want the window to be destroyed when we are destroyed
00498     XAddToSaveSet( qt_xdisplay(), client );
00499     XSelectInput( qt_xdisplay(), client, NoEventMask );
00500     XUnmapWindow( qt_xdisplay(), client );
00501     XWindowChanges wc;     // set the border width to 0
00502     wc.border_width = 0; // TODO possibly save this, and also use it for initial configuring of the window
00503     XConfigureWindow( qt_xdisplay(), client, CWBorderWidth, &wc );
00504 
00505     XSetWindowAttributes swa;
00506     swa.colormap = attr.colormap;
00507     swa.background_pixmap = None;
00508     swa.border_pixel = 0;
00509 
00510     frame = XCreateWindow( qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0,
00511             attr.depth, InputOutput, attr.visual,
00512             CWColormap | CWBackPixmap | CWBorderPixel, &swa );
00513     wrapper = XCreateWindow( qt_xdisplay(), frame, 0, 0, 1, 1, 0,
00514             attr.depth, InputOutput, attr.visual,
00515             CWColormap | CWBackPixmap | CWBorderPixel, &swa );
00516 
00517     XDefineCursor( qt_xdisplay(), frame, arrowCursor.handle());
00518     // some apps are stupid and don't define their own cursor - set the arrow one for them
00519     XDefineCursor( qt_xdisplay(), wrapper, arrowCursor.handle());
00520     XReparentWindow( qt_xdisplay(), client, wrapper, 0, 0 );
00521     XSelectInput( qt_xdisplay(), frame,
00522             KeyPressMask | KeyReleaseMask |
00523             ButtonPressMask | ButtonReleaseMask |
00524             KeymapStateMask |
00525             ButtonMotionMask |
00526             PointerMotionMask |
00527             EnterWindowMask | LeaveWindowMask |
00528             FocusChangeMask |
00529             ExposureMask |
00530             PropertyChangeMask |
00531             StructureNotifyMask | SubstructureRedirectMask |
00532             VisibilityChangeMask );
00533     XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00534     XSelectInput( qt_xdisplay(), client,
00535                   FocusChangeMask |
00536                   PropertyChangeMask |
00537                   ColormapChangeMask |
00538                   EnterWindowMask | LeaveWindowMask |
00539                   KeyPressMask | KeyReleaseMask
00540                   );
00541     updateMouseGrab();
00542     }
00543 
00544 } // namespace
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Apr 11 13:44:53 2004 by doxygen 1.3.6-20040222 written by Dimitri van Heesch, © 1997-2003