add more spacing
[personal-kdebase.git] / workspace / kwin / manage.cpp
blobfe126f75f2f0a3c2ce95937d896f8a46289c0877
1 /********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
5 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *********************************************************************/
22 // This file contains things relevant to handling incoming events.
24 #include "client.h"
26 #include <kstartupinfo.h>
27 #include <kglobal.h>
28 #include <X11/extensions/shape.h>
30 #include "notifications.h"
31 #include <QX11Info>
32 #include "rules.h"
33 #include "group.h"
35 namespace KWin
38 /**
39 * Manages the clients. This means handling the very first maprequest:
40 * reparenting, initial geometry, initial state, placement, etc.
41 * Returns false if KWin is not going to manage this window.
43 bool Client::manage( Window w, bool isMapped )
45 StackingUpdatesBlocker stacking_blocker( workspace() );
47 grabXServer();
49 XWindowAttributes attr;
50 if( !XGetWindowAttributes( display(), w, &attr ))
52 ungrabXServer();
53 return false;
56 // From this place on, manage() must not return false
57 block_geometry_updates = 1;
58 pending_geometry_update = PendingGeometryForced; // Force update when finishing with geometry changes
60 embedClient( w, attr );
62 vis = attr.visual;
63 bit_depth = attr.depth;
65 // SELI TODO: Order all these things in some sane manner
67 bool init_minimize = false;
68 XWMHints* hints = XGetWMHints( display(), w );
69 if( hints && ( hints->flags & StateHint ) && hints->initial_state == IconicState )
70 init_minimize = true;
71 if( hints )
72 XFree( hints );
73 if( isMapped )
74 init_minimize = false; // If it's already mapped, ignore hint
76 unsigned long properties[2];
77 properties[WinInfo::PROTOCOLS] =
78 NET::WMDesktop |
79 NET::WMState |
80 NET::WMWindowType |
81 NET::WMStrut |
82 NET::WMName |
83 NET::WMIconGeometry |
84 NET::WMIcon |
85 NET::WMPid |
86 NET::WMIconName |
88 properties[WinInfo::PROTOCOLS2] =
89 NET::WM2UserTime |
90 NET::WM2StartupId |
91 NET::WM2ExtendedStrut |
92 NET::WM2Opacity |
93 NET::WM2FullscreenMonitors |
96 info = new WinInfo( this, display(), client, rootWindow(), properties, 2 );
98 cmap = attr.colormap;
100 getResourceClass();
101 getWindowRole();
102 getWmClientLeader();
103 getWmClientMachine();
104 getSyncCounter();
105 // First only read the caption text, so that setupWindowRules() can use it for matching,
106 // and only then really set the caption using setCaption(), which checks for duplicates etc.
107 // and also relies on rules already existing
108 cap_normal = readName();
109 setupWindowRules( false );
110 ignore_focus_stealing = options->checkIgnoreFocusStealing( this ); // TODO: Change to rules
111 setCaption( cap_normal, true );
113 if( Extensions::shapeAvailable() )
114 XShapeSelectInput( display(), window(), ShapeNotifyMask );
115 detectShape( window() );
116 detectNoBorder();
117 fetchIconicName();
118 getWMHints(); // Needs to be done before readTransient() because of reading the group
119 modal = ( info->state() & NET::Modal ) != 0; // Needs to be valid before handling groups
120 readTransient();
121 getIcons();
122 getWindowProtocols();
123 getWmNormalHints(); // Get xSizeHint
124 getMotifHints();
126 // TODO: Try to obey all state information from info->state()
128 original_skip_taskbar = skip_taskbar = ( info->state() & NET::SkipTaskbar ) != 0;
129 skip_pager = ( info->state() & NET::SkipPager ) != 0;
131 setupCompositing();
133 KStartupInfoId asn_id;
134 KStartupInfoData asn_data;
135 bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data );
137 workspace()->updateClientLayer( this );
139 SessionInfo* session = workspace()->takeSessionInfo( this );
140 if( session )
142 init_minimize = session->minimized;
143 noborder = session->noBorder;
146 setShortcut( rules()->checkShortcut( session ? session->shortcut : QString(), true ));
148 init_minimize = rules()->checkMinimize( init_minimize, !isMapped );
149 noborder = rules()->checkNoBorder( noborder, !isMapped );
151 // Initial desktop placement
152 if( session )
154 desk = session->desktop;
155 if( session->onAllDesktops )
156 desk = NET::OnAllDesktops;
158 else
160 // If this window is transient, ensure that it is opened on the
161 // same window as its parent. this is necessary when an application
162 // starts up on a different desktop than is currently displayed
163 if( isTransient() )
165 ClientList mainclients = mainClients();
166 bool on_current = false;
167 Client* maincl = NULL;
168 // This is slightly duplicated from Placement::placeOnMainWindow()
169 for( ClientList::ConstIterator it = mainclients.constBegin();
170 it != mainclients.constEnd();
171 ++it )
173 if( mainclients.count() > 1 && (*it)->isSpecialWindow() )
174 continue; // Don't consider toolbars etc when placing
175 maincl = *it;
176 if( (*it)->isOnCurrentDesktop() )
177 on_current = true;
179 if( on_current )
180 desk = workspace()->currentDesktop();
181 else if( maincl != NULL )
182 desk = maincl->desktop();
184 if( info->desktop() )
185 desk = info->desktop(); // Window had the initial desktop property, force it
186 if( desktop() == 0 && asn_valid && asn_data.desktop() != 0 )
187 desk = asn_data.desktop();
189 if( desk == 0 ) // Assume window wants to be visible on the current desktop
190 desk = workspace()->currentDesktop();
191 desk = rules()->checkDesktop( desk, !isMapped );
192 if( desk != NET::OnAllDesktops ) // Do range check
193 desk = qMax( 1, qMin( workspace()->numberOfDesktops(), desk ));
194 info->setDesktop( desk );
195 workspace()->updateOnAllDesktopsOfTransients( this ); // SELI TODO
196 //onAllDesktopsChange(); // Decoration doesn't exist here yet
198 QRect geom( attr.x, attr.y, attr.width, attr.height );
199 bool placementDone = false;
201 if ( session )
202 geom = session->geometry;
204 QRect area;
205 bool partial_keep_in_area = isMapped || session;
206 if( isMapped || session )
207 area = workspace()->clientArea( FullArea, geom.center(), desktop() );
208 else if( options->xineramaPlacementEnabled )
210 int screen = options->xineramaPlacementScreen;
211 if( screen == -1 ) // Active screen
212 screen = asn_data.xinerama() == -1 ? workspace()->activeScreen() : asn_data.xinerama();
213 area = workspace()->clientArea( PlacementArea, workspace()->screenGeometry( screen ).center(), desktop());
215 else
216 area = workspace()->clientArea( PlacementArea, cursorPos(), desktop() );
218 if( int type = checkFullScreenHack( geom ))
220 fullscreen_mode = FullScreenHack;
221 if( rules()->checkStrictGeometry( false ))
223 geom = type == 2 // 1 = It's xinerama-aware fullscreen hack, 2 = It's full area
224 ? workspace()->clientArea( FullArea, geom.center(), desktop() )
225 : workspace()->clientArea( ScreenArea, geom.center(), desktop() );
227 else
228 geom = workspace()->clientArea( FullScreenArea, geom.center(), desktop() );
229 placementDone = true;
232 if( isDesktop() )
233 // KWin doesn't manage desktop windows
234 placementDone = true;
236 bool usePosition = false;
237 if ( isMapped || session || placementDone )
238 placementDone = true; // Use geometry
239 else if( isTransient() && !isUtility() && !isDialog() && !isSplash() )
240 usePosition = true;
241 else if( isTransient() && !hasNETSupport() )
242 usePosition = true;
243 else if( isDialog() && hasNETSupport() )
244 { // If the dialog is actually non-NETWM transient window, don't try to apply placement to it,
245 // it breaks with too many things (xmms, display)
246 if( mainClients().count() >= 1 )
248 #if 1
249 // #78082 - Ok, it seems there are after all some cases when an application has a good
250 // reason to specify a position for its dialog. Too bad other WMs have never bothered
251 // with placement for dialogs, so apps always specify positions for their dialogs,
252 // including such silly positions like always centered on the screen or under mouse.
253 // Using ignoring requested position in window-specific settings helps, and now
254 // there's also _NET_WM_FULL_PLACEMENT.
255 usePosition = true;
256 #else
257 ; // Force using placement policy
258 #endif
260 else
261 usePosition = true;
263 else if( isSplash() )
264 ; // Force using placement policy
265 else
266 usePosition = true;
267 if( !rules()->checkIgnoreGeometry( !usePosition ))
269 bool ignorePPosition = options->ignorePositionClasses.contains(
270 QString::fromLatin1( resourceClass() ));
272 if((( xSizeHint.flags & PPosition ) && !ignorePPosition ) ||
273 ( xSizeHint.flags & USPosition ))
275 placementDone = true;
276 // Disobey xinerama placement option for now (#70943)
277 area = workspace()->clientArea( PlacementArea, geom.center(), desktop() );
280 //if( true ) // Size is always obeyed for now, only with constraints applied
281 // if(( xSizeHint.flags & USSize ) || ( xSizeHint.flags & PSize ))
282 // {
283 // // Keep in mind that we now actually have a size :-)
284 // }
286 if( xSizeHint.flags & PMaxSize )
287 geom.setSize( geom.size().boundedTo(
288 rules()->checkMaxSize( QSize( xSizeHint.max_width, xSizeHint.max_height ))));
289 if( xSizeHint.flags & PMinSize )
290 geom.setSize( geom.size().expandedTo(
291 rules()->checkMinSize( QSize( xSizeHint.min_width, xSizeHint.min_height ))));
293 if( isMovable() && ( geom.x() > area.right() || geom.y() > area.bottom() ))
294 placementDone = false; // Weird, do not trust.
296 if( placementDone )
297 move( geom.x(), geom.y() ); // Before gravitating
299 updateDecoration( false ); // Also gravitates
300 // TODO: Is CentralGravity right here, when resizing is done after gravitating?
301 plainResize( rules()->checkSize( sizeForClientSize( geom.size() ), !isMapped ));
303 QPoint forced_pos = rules()->checkPosition( invalidPoint, !isMapped );
304 if( forced_pos != invalidPoint )
306 move( forced_pos );
307 placementDone = true;
308 // Don't keep inside workarea if the window has specially configured position
309 partial_keep_in_area = true;
310 area = workspace()->clientArea( FullArea, geom.center(), desktop() );
312 if( !placementDone )
313 { // Placement needs to be after setting size
314 workspace()->place( this, area );
315 placementDone = true;
318 if(( !isSpecialWindow() || isToolbar() ) && isMovable() )
319 keepInArea( area, partial_keep_in_area );
321 updateShape();
323 // CT: Extra check for stupid jdk 1.3.1. But should make sense in general
324 // if client has initial state set to Iconic and is transient with a parent
325 // window that is not Iconic, set init_state to Normal
326 if( init_minimize && isTransient() )
328 ClientList mainclients = mainClients();
329 for( ClientList::ConstIterator it = mainclients.constBegin();
330 it != mainclients.constEnd();
331 ++it )
332 if( (*it)->isShown( true ))
333 init_minimize = false; // SELI TODO: Even e.g. for NET::Utility?
335 // If a dialog is shown for minimized window, minimize it too
336 if( !init_minimize && isTransient() && mainClients().count() > 0 )
338 bool visible_parent = false;
339 // Use allMainClients(), to include also main clients of group transients
340 // that have been optimized out in Client::checkGroupTransients()
341 ClientList mainclients = allMainClients();
342 for( ClientList::ConstIterator it = mainclients.constBegin();
343 it != mainclients.constEnd();
344 ++it )
345 if( (*it)->isShown( true ))
346 visible_parent = true;
347 if( !visible_parent )
349 init_minimize = true;
350 demandAttention();
354 if( init_minimize )
355 minimize( true ); // No animation
357 // SELI TODO: This seems to be mainly for kstart and ksystraycmd
358 // probably should be replaced by something better
359 bool doNotShow = false;
360 if ( workspace()->isNotManaged( caption() ))
361 doNotShow = true;
363 // Other settings from the previous session
364 if( session )
366 // Session restored windows are not considered to be new windows WRT rules,
367 // I.e. obey only forcing rules
368 setKeepAbove( session->keepAbove );
369 setKeepBelow( session->keepBelow );
370 setSkipTaskbar( session->skipTaskbar, true );
371 setSkipPager( session->skipPager );
372 setShade( session->shaded ? ShadeNormal : ShadeNone );
373 if( session->maximized != MaximizeRestore )
375 maximize( MaximizeMode( session->maximized ));
376 geom_restore = session->restore;
378 if( session->fullscreen == FullScreenHack )
379 ; // Nothing, this should be already set again above
380 else if( session->fullscreen != FullScreenNone )
382 setFullScreen( true, false );
383 geom_fs_restore = session->fsrestore;
386 else
388 geom_restore = geometry(); // Remember restore geometry
389 if( isMaximizable() && ( width() >= area.width() || height() >= area.height() ))
390 { // Window is too large for the screen, maximize in the
391 // directions necessary
392 if( width() >= area.width() && height() >= area.height() )
394 maximize( Client::MaximizeFull );
395 geom_restore = QRect(); // Use placement when unmaximizing
397 else if( width() >= area.width() )
399 maximize( Client::MaximizeHorizontal );
400 geom_restore = QRect(); // Use placement when unmaximizing
401 geom_restore.setY( y() ); // But only for horizontal direction
402 geom_restore.setHeight( height() );
404 else if( height() >= area.height() )
406 maximize( Client::MaximizeVertical );
407 geom_restore = QRect(); // Use placement when unmaximizing
408 geom_restore.setX( x() ); // But only for vertical direction
409 geom_restore.setWidth( width() );
413 // Window may want to be maximized
414 // done after checking that the window isn't larger than the workarea, so that
415 // the restore geometry from the checks above takes precedence, and window
416 // isn't restored larger than the workarea
417 MaximizeMode maxmode = static_cast<MaximizeMode>(
418 (( info->state() & NET::MaxVert ) ? MaximizeVertical : 0 ) |
419 (( info->state() & NET::MaxHoriz ) ? MaximizeHorizontal : 0 ));
420 MaximizeMode forced_maxmode = rules()->checkMaximize( maxmode, !isMapped );
422 // Either hints were set to maximize, or is forced to maximize,
423 // or is forced to non-maximize and hints were set to maximize
424 if( forced_maxmode != MaximizeRestore || maxmode != MaximizeRestore )
425 maximize( forced_maxmode );
427 // Read other initial states
428 setShade( rules()->checkShade( info->state() & NET::Shaded ? ShadeNormal : ShadeNone, !isMapped ));
429 setKeepAbove( rules()->checkKeepAbove( info->state() & NET::KeepAbove, !isMapped ));
430 setKeepBelow( rules()->checkKeepBelow( info->state() & NET::KeepBelow, !isMapped ));
431 setSkipTaskbar( rules()->checkSkipTaskbar( info->state() & NET::SkipTaskbar, !isMapped ), true );
432 setSkipPager( rules()->checkSkipPager( info->state() & NET::SkipPager, !isMapped ));
433 if( info->state() & NET::DemandsAttention )
434 demandAttention();
435 if( info->state() & NET::Modal )
436 setModal( true );
437 if( fullscreen_mode != FullScreenHack && isFullScreenable() )
438 setFullScreen( rules()->checkFullScreen( info->state() & NET::FullScreen, !isMapped ), false );
441 updateAllowedActions( true );
443 // Set initial user time directly
444 user_time = readUserTimeMapTimestamp( asn_valid ? &asn_id : NULL, asn_valid ? &asn_data : NULL, session );
445 group()->updateUserTime( user_time ); // And do what Client::updateUserTime() does
447 if( isTopMenu()) // They're shown in Workspace::addClient() if their mainwindow
448 hideClient( true ); // Is the active one
450 // This should avoid flicker, because real restacking is done
451 // only after manage() finishes because of blocking, but the window is shown sooner
452 XLowerWindow( display(), frameId() );
453 if( session && session->stackingOrder != -1 )
455 sm_stacking_order = session->stackingOrder;
456 workspace()->restoreSessionStackingOrder( this );
459 if( compositing() )
460 // Sending ConfigureNotify is done when setting mapping state below,
461 // Getting the first sync response means window is ready for compositing
462 sendSyncRequest();
464 if( isShown( true ) && !doNotShow )
466 if( isDialog() )
467 Notify::raise( Notify::TransNew );
468 if( isNormalWindow() )
469 Notify::raise( Notify::New );
471 bool allow;
472 if( session )
473 allow = session->active &&
474 ( !workspace()->wasUserInteraction() || workspace()->activeClient() == NULL ||
475 workspace()->activeClient()->isDesktop() );
476 else
477 allow = workspace()->allowClientActivation( this, userTime(), false );
479 // If session saving, force showing new windows (i.e. "save file?" dialogs etc.)
480 // also force if activation is allowed
481 if( !isOnCurrentDesktop() && !isMapped && !session && ( allow || workspace()->sessionSaving() ))
482 workspace()->setCurrentDesktop( desktop() );
484 bool belongs_to_desktop = false;
485 for( ClientList::ConstIterator it = group()->members().constBegin();
486 it != group()->members().constEnd();
487 ++it )
488 if( (*it)->isDesktop() )
490 belongs_to_desktop = true;
491 break;
493 if( !belongs_to_desktop && workspace()->showingDesktop() )
494 workspace()->resetShowingDesktop( options->showDesktopIsMinimizeAll );
496 if( isOnCurrentDesktop() && !isMapped && !allow && ( !session || session->stackingOrder < 0 ))
497 workspace()->restackClientUnderActive( this );
499 updateVisibility();
501 if( !isMapped )
503 if( allow && isOnCurrentDesktop() )
505 if( !isSpecialWindow() )
506 if ( options->focusPolicyIsReasonable() && wantsTabFocus() )
507 workspace()->requestFocus( this );
509 else if( !session && !isSpecialWindow())
510 demandAttention();
513 else if( !doNotShow ) // if( !isShown( true ) && !doNotShow )
514 updateVisibility();
515 else // doNotShow
516 hideClient( true ); // SELI HACK !!!
517 assert( mapping_state != Withdrawn );
518 blockGeometryUpdates( false );
520 if( user_time == CurrentTime || user_time == -1U )
521 { // No known user time, set something old
522 user_time = xTime() - 1000000;
523 if( user_time == CurrentTime || user_time == -1U ) // Let's be paranoid
524 user_time = xTime() - 1000000 + 10;
527 updateWorkareaDiffs();
529 //sendSyntheticConfigureNotify(); // Done when setting mapping state
531 delete session;
533 ungrabXServer();
535 client_rules.discardTemporary();
536 applyWindowRules(); // Just in case
537 workspace()->discardUsedWindowRules( this, false ); // Remove ApplyNow rules
538 updateWindowRules(); // Was blocked while !isManaged()
540 // TODO: there's a small problem here - isManaged() depends on the mapping state,
541 // but this client is not yet in Workspace's client list at this point, will
542 // be only done in addClient()
543 return true;
546 // Called only from manage()
547 void Client::embedClient( Window w, const XWindowAttributes& attr )
549 assert( client == None );
550 assert( frameId() == None );
551 assert( wrapper == None );
552 client = w;
554 // We don't want the window to be destroyed when we are destroyed
555 XAddToSaveSet( display(), client );
556 XSelectInput( display(), client, NoEventMask );
557 XUnmapWindow( display(), client );
558 XWindowChanges wc; // Set the border width to 0
559 wc.border_width = 0; // TODO: Possibly save this, and also use it for initial configuring of the window
560 XConfigureWindow( display(), client, CWBorderWidth, &wc );
562 XSetWindowAttributes swa;
563 swa.colormap = attr.colormap;
564 swa.background_pixmap = None;
565 swa.border_pixel = 0;
567 Window frame = XCreateWindow( display(), rootWindow(), 0, 0, 1, 1, 0,
568 attr.depth, InputOutput, attr.visual, CWColormap | CWBackPixmap | CWBorderPixel, &swa );
569 setWindowHandles( client, frame );
570 wrapper = XCreateWindow( display(), frame, 0, 0, 1, 1, 0,
571 attr.depth, InputOutput, attr.visual, CWColormap | CWBackPixmap | CWBorderPixel, &swa );
573 XDefineCursor( display(), frame, QCursor( Qt::ArrowCursor ).handle() );
574 // Some apps are stupid and don't define their own cursor - set the arrow one for them
575 XDefineCursor( display(), wrapper, QCursor( Qt::ArrowCursor ).handle() );
576 XReparentWindow( display(), client, wrapper, 0, 0 );
577 XSelectInput( display(), frame,
578 KeyPressMask | KeyReleaseMask |
579 ButtonPressMask | ButtonReleaseMask |
580 KeymapStateMask |
581 ButtonMotionMask |
582 PointerMotionMask |
583 EnterWindowMask | LeaveWindowMask |
584 FocusChangeMask |
585 ExposureMask |
586 PropertyChangeMask |
587 StructureNotifyMask | SubstructureRedirectMask );
588 XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask );
589 XSelectInput( display(), client,
590 FocusChangeMask |
591 PropertyChangeMask |
592 ColormapChangeMask |
593 EnterWindowMask | LeaveWindowMask |
594 KeyPressMask | KeyReleaseMask
597 updateMouseGrab();
600 } // namespace