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 Thu Sep 16 15:59:32 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003