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 ; // force using placement policy 00225 else if( isSplash()) 00226 ; // force using placement policy 00227 else 00228 usePosition = true; 00229 if( !rules()->checkIgnorePosition( !usePosition )) 00230 { 00231 bool ignorePPosition = ( options->ignorePositionClasses.contains(QString::fromLatin1(resourceClass()))); 00232 00233 if ( ( (xSizeHint.flags & PPosition) && !ignorePPosition ) || 00234 (xSizeHint.flags & USPosition) ) 00235 { 00236 placementDone = TRUE; 00237 // disobey xinerama placement option for now (#70943) 00238 area = workspace()->clientArea( PlacementArea, geom.center(), desktop()); 00239 } 00240 } 00241 if( true ) // size is always obeyed for now, only with constraints applied 00242 if ( (xSizeHint.flags & USSize) || (xSizeHint.flags & PSize) ) 00243 { 00244 // keep in mind that we now actually have a size :-) 00245 } 00246 00247 if (xSizeHint.flags & PMaxSize) 00248 geom.setSize( geom.size().boundedTo( 00249 rules()->checkMaxSize( QSize(xSizeHint.max_width, xSizeHint.max_height ) ) ) ); 00250 if (xSizeHint.flags & PMinSize) 00251 geom.setSize( geom.size().expandedTo( 00252 rules()->checkMinSize( QSize(xSizeHint.min_width, xSizeHint.min_height ) ) ) ); 00253 00254 if( isMovable()) 00255 { 00256 if( geom.x() > area.right() || geom.y() > area.bottom()) 00257 placementDone = FALSE; // weird, do not trust. 00258 } 00259 00260 if ( placementDone ) 00261 move( geom.x(), geom.y() ); // before gravitating 00262 00263 updateDecoration( false ); // also gravitates 00264 // TODO is CentralGravity right here, when resizing is done after gravitating? 00265 plainResize( rules()->checkSize( sizeForClientSize( geom.size()), !isMapped )); 00266 00267 QPoint forced_pos = rules()->checkPosition( invalidPoint, !isMapped ); 00268 if( forced_pos != invalidPoint ) 00269 { 00270 move( forced_pos ); 00271 placementDone = true; 00272 } 00273 if( !placementDone ) 00274 { // placement needs to be after setting size 00275 workspace()->place( this, area ); 00276 placementDone = TRUE; 00277 } 00278 00279 if( !isMapped && !session // trust position from session or if already mapped 00280 && ( !isSpecialWindow() || isToolbar()) && isMovable()) 00281 keepInArea( area ); 00282 00283 XShapeSelectInput( qt_xdisplay(), window(), ShapeNotifyMask ); 00284 if ( (is_shape = Shape::hasShape( window())) ) 00285 { 00286 updateShape(); 00287 } 00288 00289 //CT extra check for stupid jdk 1.3.1. But should make sense in general 00290 // if client has initial state set to Iconic and is transient with a parent 00291 // window that is not Iconic, set init_state to Normal 00292 if( init_minimize && isTransient()) 00293 { 00294 ClientList mainclients = mainClients(); 00295 for( ClientList::ConstIterator it = mainclients.begin(); 00296 it != mainclients.end(); 00297 ++it ) 00298 if( (*it)->isShown( true )) 00299 init_minimize = false; // SELI even e.g. for NET::Utility? 00300 } 00301 00302 if( init_minimize ) 00303 minimize( true ); // no animation 00304 00305 // SELI this seems to be mainly for kstart and ksystraycmd 00306 // probably should be replaced by something better 00307 bool doNotShow = false; 00308 if ( workspace()->isNotManaged( caption() ) ) 00309 doNotShow = TRUE; 00310 00311 // other settings from the previous session 00312 if ( session ) 00313 { 00314 // session restored windows are not considered to be new windows WRT rules, 00315 // i.e. obey only forcing rules 00316 setKeepAbove( session->keepAbove ); 00317 setKeepBelow( session->keepBelow ); 00318 setSkipTaskbar( session->skipTaskbar, true ); 00319 setSkipPager( session->skipPager ); 00320 setShade( session->shaded ? ShadeNormal : ShadeNone ); 00321 if( session->maximized != MaximizeRestore ) 00322 { 00323 maximize( (MaximizeMode) session->maximized ); 00324 geom_restore = session->restore; 00325 } 00326 if( session->fullscreen == FullScreenHack ) 00327 ; // nothing, this should be already set again above 00328 else if( session->fullscreen != FullScreenNone ) 00329 { 00330 setFullScreen( true, false ); 00331 geom_fs_restore = session->fsrestore; 00332 } 00333 } 00334 else 00335 { 00336 geom_restore = geometry(); // remember restore geometry 00337 if ( isMaximizable() 00338 && ( width() >= area.width() || height() >= area.height() ) ) 00339 { 00340 // window is too large for the screen, maximize in the 00341 // directions necessary 00342 if ( width() >= area.width() && height() >= area.height() ) 00343 { 00344 maximize( Client::MaximizeFull ); 00345 geom_restore = QRect(); // use placement when unmaximizing 00346 } 00347 else if ( width() >= area.width() ) 00348 { 00349 maximize( Client::MaximizeHorizontal ); 00350 geom_restore = QRect(); // use placement when unmaximizing 00351 geom_restore.setY( y()); // but only for horizontal direction 00352 geom_restore.setHeight( height()); 00353 } 00354 else if ( height() >= area.height() ) 00355 { 00356 maximize( Client::MaximizeVertical ); 00357 geom_restore = QRect(); // use placement when unmaximizing 00358 geom_restore.setX( x()); // but only for vertical direction 00359 geom_restore.setWidth( width()); 00360 } 00361 } 00362 // window may want to be maximized 00363 // done after checking that the window isn't larger than the workarea, so that 00364 // the restore geometry from the checks above takes precedence, and window 00365 // isn't restored larger than the workarea 00366 MaximizeMode maxmode = static_cast< MaximizeMode > 00367 ((( info->state() & NET::MaxVert ) ? MaximizeVertical : 0 ) 00368 | (( info->state() & NET::MaxHoriz ) ? MaximizeHorizontal : 0 )); 00369 MaximizeMode forced_maxmode = rules()->checkMaximize( maxmode, !isMapped ); 00370 // either hints were set to maximize, or is forced to maximize, 00371 // or is forced to non-maximize and hints were set to maximize 00372 if( forced_maxmode != MaximizeRestore || maxmode != MaximizeRestore ) 00373 maximize( forced_maxmode ); 00374 00375 // read other initial states 00376 setShade( rules()->checkShade( info->state() & NET::Shaded ? ShadeNormal : ShadeNone, !isMapped )); 00377 setKeepAbove( rules()->checkKeepAbove( info->state() & NET::KeepAbove, !isMapped )); 00378 setKeepBelow( rules()->checkKeepBelow( info->state() & NET::KeepBelow, !isMapped )); 00379 setSkipTaskbar( rules()->checkSkipTaskbar( info->state() & NET::SkipTaskbar, !isMapped ), true ); 00380 setSkipPager( rules()->checkSkipPager( info->state() & NET::SkipPager, !isMapped )); 00381 if( info->state() & NET::DemandsAttention ) 00382 demandAttention(); 00383 if( info->state() & NET::Modal ) 00384 setModal( true ); 00385 if( fullscreen_mode != FullScreenHack && isFullScreenable()) 00386 setFullScreen( rules()->checkFullScreen( info->state() & NET::FullScreen, !isMapped ), false ); 00387 } 00388 00389 updateAllowedActions( true ); 00390 00391 // TODO this should avoid flicker, because real restacking is done 00392 // only after manage() finishes, but the window is shown sooner 00393 // - keep it? 00394 XLowerWindow( qt_xdisplay(), frameId()); 00395 00396 user_time = readUserTimeMapTimestamp( asn_valid ? &asn_id : NULL, asn_valid ? &asn_data : NULL, session ); 00397 00398 if( isTopMenu()) // they're shown in Workspace::addClient() if their mainwindow 00399 hideClient( true ); // is the active one 00400 00401 if ( isShown( true ) && !doNotShow ) 00402 { 00403 if( isDialog()) 00404 Notify::raise( Notify::TransNew ); 00405 if( isNormalWindow()) 00406 Notify::raise( Notify::New ); 00407 00408 bool allow; 00409 if( session ) 00410 allow = session->active && !workspace()->wasUserInteraction(); 00411 else 00412 allow = workspace()->allowClientActivation( this, userTime(), false ); 00413 00414 // if session saving, force showing new windows (i.e. "save file?" dialogs etc.) 00415 // also force if activation is allowed 00416 if( !isOnCurrentDesktop() && !isMapped && !session && ( allow || workspace()->sessionSaving())) 00417 workspace()->setCurrentDesktop( desktop()); 00418 00419 if( isOnCurrentDesktop()) 00420 { 00421 setMappingState( NormalState ); 00422 00423 if( isMapped ) 00424 { 00425 workspace()->raiseClient( this ); 00426 rawShow(); 00427 } 00428 else 00429 { 00430 if( allow ) 00431 { 00432 workspace()->raiseClient( this ); 00433 rawShow(); 00434 if( !isSpecialWindow() || isOverride()) 00435 if ( options->focusPolicyIsReasonable() && wantsTabFocus() ) 00436 workspace()->requestFocus( this ); 00437 } 00438 else 00439 { 00440 workspace()->restackClientUnderActive( this ); 00441 rawShow(); 00442 if( !session && ( !isSpecialWindow() || isOverride())) 00443 demandAttention(); 00444 } 00445 } 00446 } 00447 else 00448 { 00449 virtualDesktopChange(); 00450 workspace()->raiseClient( this ); 00451 if( !session && !isMapped ) 00452 demandAttention(); 00453 } 00454 } 00455 else if( !doNotShow ) // !isShown() 00456 { 00457 rawHide(); 00458 setMappingState( IconicState ); 00459 } 00460 else // doNotShow 00461 { // SELI HACK !!! 00462 hideClient( true ); 00463 setMappingState( IconicState ); 00464 } 00465 assert( mappingState() != WithdrawnState ); 00466 00467 if( user_time == CurrentTime || user_time == -1U ) // no known user time, set something old 00468 { 00469 user_time = qt_x_time - 1000000; 00470 if( user_time == CurrentTime || user_time == -1U ) // let's be paranoid 00471 user_time = qt_x_time - 1000000 + 10; 00472 } 00473 00474 updateWorkareaDiffs(); 00475 00476 // sendSyntheticConfigureNotify(); done when setting mapping state 00477 00478 delete session; 00479 00480 checkActiveModal(); 00481 00482 ungrabXServer(); 00483 00484 client_rules.discardTemporary(); 00485 updateWindowRules(); // was blocked while !isManaged() 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.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Aug 13 21:47:05 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003