summaryrefslogtreecommitdiffstats
path: root/twin/manage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'twin/manage.cpp')
-rw-r--r--twin/manage.cpp606
1 files changed, 606 insertions, 0 deletions
diff --git a/twin/manage.cpp b/twin/manage.cpp
new file mode 100644
index 000000000..ec6a5d675
--- /dev/null
+++ b/twin/manage.cpp
@@ -0,0 +1,606 @@
+/*****************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
+Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
+
+You can Freely distribute this program under the GNU General Public
+License. See the file "COPYING" for the exact licensing terms.
+******************************************************************/
+
+/*
+
+ This file contains things relevant to handling incoming events.
+
+*/
+
+#include "client.h"
+
+#include <tdestartupinfo.h>
+#include <tdeglobal.h>
+#include <X11/extensions/shape.h>
+
+#include "notifications.h"
+#include "rules.h"
+#include "group.h"
+
+namespace KWinInternal
+{
+
+/*!
+ Manages the clients. This means handling the very first maprequest:
+ reparenting, initial geometry, initial state, placement, etc.
+ Returns false if KWin is not going to manage this window.
+ */
+bool Client::manage( Window w, bool isMapped )
+ {
+ XWindowAttributes attr;
+ if( !XGetWindowAttributes(tqt_xdisplay(), w, &attr))
+ return false;
+
+ grabXServer();
+
+ // from this place on, manage() mustn't return false
+ postpone_geometry_updates = 1;
+ pending_geometry_update = true; // force update when finishing with geometry changes
+
+ embedClient( w, attr );
+
+ // SELI order all these things in some sane manner
+
+ bool init_minimize = false;
+ XWMHints * hints = XGetWMHints(tqt_xdisplay(), w );
+ if (hints && (hints->flags & StateHint) && hints->initial_state == IconicState)
+ init_minimize = true;
+ if (hints)
+ XFree(hints);
+ if( isMapped )
+ init_minimize = false; // if it's already mapped, ignore hint
+
+ unsigned long properties[ 2 ];
+ properties[ WinInfo::PROTOCOLS ] =
+ NET::WMDesktop |
+ NET::WMState |
+ NET::WMWindowType |
+ NET::WMStrut |
+ NET::WMName |
+ NET::WMIconGeometry |
+ NET::WMIcon |
+ NET::WMPid |
+ NET::WMIconName |
+ 0;
+ properties[ WinInfo::PROTOCOLS2 ] =
+ NET::WM2UserTime |
+ NET::WM2StartupId |
+ NET::WM2ExtendedStrut |
+ 0;
+
+ info = new WinInfo( this, tqt_xdisplay(), client, tqt_xrootwin(), properties, 2 );
+
+ cmap = attr.colormap;
+
+ XClassHint classHint;
+ if ( XGetClassHint( tqt_xdisplay(), client, &classHint ) )
+ {
+ // Qt3.2 and older had this all lowercase, Qt3.3 capitalized resource class
+ // force lowercase, so that workarounds listing resource classes still work
+ resource_name = TQCString( classHint.res_name ).lower();
+ resource_class = TQCString( classHint.res_class ).lower();
+ XFree( classHint.res_name );
+ XFree( classHint.res_class );
+ }
+ ignore_focus_stealing = options->checkIgnoreFocusStealing( this ); // TODO change to rules
+
+ window_role = staticWindowRole( w );
+ getWmClientLeader();
+ getWmClientMachine();
+ // first only read the caption text, so that setupWindowRules() can use it for matching,
+ // and only then really set the caption using setCaption(), which checks for duplicates etc.
+ // and also relies on rules already existing
+ cap_normal = readName();
+ setupWindowRules( false );
+ setCaption( cap_normal, true );
+
+ detectNoBorder();
+ detectShapable();
+ fetchIconicName();
+ getWMHints(); // needs to be done before readTransient() because of reading the group
+ modal = ( info->state() & NET::Modal ) != 0; // needs to be valid before handling groups
+ readTransient();
+ getIcons();
+ getWindowProtocols();
+ getWmNormalHints(); // get xSizeHint
+ getMotifHints();
+
+ // TODO try to obey all state information from info->state()
+
+ original_skip_taskbar = skip_taskbar = ( info->state() & NET::SkipTaskbar) != 0;
+ skip_pager = ( info->state() & NET::SkipPager) != 0;
+
+ TDEStartupInfoId asn_id;
+ TDEStartupInfoData asn_data;
+ bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data );
+
+ workspace()->updateClientLayer( this );
+
+ SessionInfo* session = workspace()->takeSessionInfo( this );
+
+ if ( session )
+ {
+ if ( session->minimized )
+ init_minimize = true;
+ if( session->userNoBorder )
+ setUserNoBorder( true );
+ }
+
+ setShortcut( rules()->checkShortcut( session ? session->shortcut : TQString::null, true ));
+
+ init_minimize = rules()->checkMinimize( init_minimize, !isMapped );
+ if( rules()->checkNoBorder( false, !isMapped ))
+ setUserNoBorder( true );
+
+ checkAndSetInitialRuledOpacity();
+
+ // initial desktop placement
+ if ( session )
+ {
+ desk = session->desktop;
+ if( session->onAllDesktops )
+ desk = NET::OnAllDesktops;
+ }
+ else
+ {
+ // if this window is transient, ensure that it is opened on the
+ // same window as its parent. this is necessary when an application
+ // starts up on a different desktop than is currently displayed
+ if( isTransient())
+ {
+ ClientList mainclients = mainClients();
+ bool on_current = false;
+ Client* maincl = NULL;
+ // this is slightly duplicated from Placement::placeOnMainWindow()
+ for( ClientList::ConstIterator it = mainclients.begin();
+ it != mainclients.end();
+ ++it )
+ {
+ if( mainclients.count() > 1 && (*it)->isSpecialWindow())
+ continue; // don't consider toolbars etc when placing
+ maincl = *it;
+ if( (*it)->isOnCurrentDesktop())
+ on_current = true;
+ }
+ if( on_current )
+ desk = workspace()->currentDesktop();
+ else if( maincl != NULL )
+ desk = maincl->desktop();
+ }
+ if ( info->desktop() )
+ desk = info->desktop(); // window had the initial desktop property, force it
+ if( desktop() == 0 && asn_valid && asn_data.desktop() != 0 )
+ desk = asn_data.desktop();
+ }
+ if ( desk == 0 ) // assume window wants to be visible on the current desktop
+ desk = workspace()->currentDesktop();
+ desk = rules()->checkDesktop( desk, !isMapped );
+ if( desk != NET::OnAllDesktops ) // do range check
+ desk = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desk ));
+ info->setDesktop( desk );
+ workspace()->updateOnAllDesktopsOfTransients( this ); // SELI
+// onAllDesktopsChange(); decoration doesn't exist here yet
+
+ TQRect geom( attr.x, attr.y, attr.width, attr.height );
+ bool placementDone = FALSE;
+
+ if ( session )
+ geom = session->geometry;
+
+ TQRect area;
+ bool partial_keep_in_area = isMapped || session;
+ if( isMapped || session )
+ area = workspace()->clientArea( FullArea, geom.center(), desktop());
+ else if( options->xineramaPlacementEnabled )
+ {
+ int screen = options->xineramaPlacementScreen;
+ if( screen == -1 ) // active screen
+ screen = asn_data.xinerama() == -1 ? workspace()->activeScreen() : asn_data.xinerama();
+ area = workspace()->clientArea( PlacementArea, workspace()->screenGeometry( screen ).center(), desktop());
+ }
+ else
+ area = workspace()->clientArea( PlacementArea, TQCursor::pos(), desktop());
+
+ if( int type = checkFullScreenHack( geom ))
+ {
+ fullscreen_mode = FullScreenHack;
+ if( rules()->checkStrictGeometry( false ))
+ {
+ geom = type == 2 // 1 - it's xinerama-aware fullscreen hack, 2 - it's full area
+ ? workspace()->clientArea( FullArea, geom.center(), desktop())
+ : workspace()->clientArea( ScreenArea, geom.center(), desktop());
+ }
+ else
+ geom = workspace()->clientArea( FullScreenArea, geom.center(), desktop());
+ placementDone = true;
+ }
+
+ if ( isDesktop() )
+ {
+ // desktops are treated slightly special
+ geom = workspace()->clientArea( FullArea, geom.center(), desktop());
+ placementDone = true;
+ }
+
+ bool usePosition = false;
+ if ( isMapped || session || placementDone )
+ placementDone = true; // use geometry
+ else if( isTransient() && !isUtility() && !isDialog() && !isSplash())
+ usePosition = true;
+ else if( isTransient() && !hasNETSupport())
+ usePosition = true;
+ else if( isDialog() && hasNETSupport())
+ // if the dialog is actually non-NETWM transient window, don't try to apply placement to it,
+ // it breaks with too many things (xmms, display)
+ {
+ if( mainClients().count() >= 1 )
+ {
+#if 1
+ // TODO #78082 - Ok, it seems there are after all some cases when an application has a good
+ // reason to specify a position for its dialog. Too bad other WMs have never bothered
+ // with placement for dialogs, so apps always specify positions for their dialogs,
+ // including such silly positions like always centered on the screen or under mouse.
+ // Using ignoring requested position in window-specific settings helps, but at least
+ // for Qt apps this should work better.
+ usePosition = true;
+#else
+ ; // force using placement policy
+#endif
+ }
+ else
+ usePosition = true;
+ }
+ else if( isSplash())
+ ; // force using placement policy
+ else
+ usePosition = true;
+ if( !rules()->checkIgnoreGeometry( !usePosition ))
+ {
+ bool ignorePPosition = ( options->ignorePositionClasses.contains(TQString::fromLatin1(resourceClass())));
+
+ if ( ( (xSizeHint.flags & PPosition) && !ignorePPosition ) ||
+ (xSizeHint.flags & USPosition) )
+ {
+ placementDone = TRUE;
+ // disobey xinerama placement option for now (#70943)
+ area = workspace()->clientArea( PlacementArea, geom.center(), desktop());
+ }
+ }
+ if( true ) // size is always obeyed for now, only with constraints applied
+ if ( (xSizeHint.flags & USSize) || (xSizeHint.flags & PSize) )
+ {
+ // keep in mind that we now actually have a size :-)
+ }
+
+ if (xSizeHint.flags & PMaxSize)
+ geom.setSize( geom.size().boundedTo(
+ rules()->checkMaxSize( TQSize(xSizeHint.max_width, xSizeHint.max_height ) ) ) );
+ if (xSizeHint.flags & PMinSize)
+ geom.setSize( geom.size().expandedTo(
+ rules()->checkMinSize( TQSize(xSizeHint.min_width, xSizeHint.min_height ) ) ) );
+
+ if( isMovable())
+ {
+ if( geom.x() > area.right() || geom.y() > area.bottom())
+ placementDone = FALSE; // weird, do not trust.
+ }
+
+ if ( placementDone )
+ move( geom.x(), geom.y() ); // before gravitating
+
+ updateDecoration( false ); // also gravitates
+ // TODO is CentralGravity right here, when resizing is done after gravitating?
+ plainResize( rules()->checkSize( sizeForClientSize( geom.size()), !isMapped ));
+
+ TQPoint forced_pos = rules()->checkPosition( invalidPoint, !isMapped );
+ if( forced_pos != invalidPoint )
+ {
+ move( forced_pos );
+ placementDone = true;
+ // don't keep inside workarea if the window has specially configured position
+ partial_keep_in_area = true;
+ area = workspace()->clientArea( FullArea, geom.center(), desktop());
+ }
+ if( !placementDone )
+ { // placement needs to be after setting size
+ workspace()->place( this, area );
+ placementDone = TRUE;
+ }
+
+ if(( !isSpecialWindow() || isToolbar()) && isMovable())
+ keepInArea( area, partial_keep_in_area );
+
+ XShapeSelectInput( tqt_xdisplay(), window(), ShapeNotifyMask );
+ is_shape = Shape::hasShape( window());
+ updateShape();
+
+ //CT extra check for stupid jdk 1.3.1. But should make sense in general
+ // if client has initial state set to Iconic and is transient with a parent
+ // window that is not Iconic, set init_state to Normal
+ if( init_minimize && isTransient())
+ {
+ ClientList mainclients = mainClients();
+ for( ClientList::ConstIterator it = mainclients.begin();
+ it != mainclients.end();
+ ++it )
+ if( (*it)->isShown( true ))
+ init_minimize = false; // SELI even e.g. for NET::Utility?
+ }
+ // if a dialog is shown for minimized window, minimize it too
+ if( !init_minimize && isTransient() && mainClients().count() > 0 )
+ {
+ bool visible_parent = false;
+ ClientList mainclients = mainClients();
+ for( ClientList::ConstIterator it = mainclients.begin();
+ it != mainclients.end();
+ ++it )
+ if( (*it)->isShown( true ))
+ visible_parent = true;
+ if( !visible_parent )
+ {
+ init_minimize = true;
+ demandAttention();
+ }
+ }
+
+ if( init_minimize )
+ minimize( true ); // no animation
+
+ // SELI this seems to be mainly for kstart and ksystraycmd
+ // probably should be replaced by something better
+ bool doNotShow = false;
+ if ( workspace()->isNotManaged( caption() ) )
+ doNotShow = TRUE;
+
+ // other settings from the previous session
+ if ( session )
+ {
+ // session restored windows are not considered to be new windows WRT rules,
+ // i.e. obey only forcing rules
+ setKeepAbove( session->keepAbove );
+ setKeepBelow( session->keepBelow );
+ setSkipTaskbar( session->skipTaskbar, true );
+ setSkipPager( session->skipPager );
+ setShade( session->shaded ? ShadeNormal : ShadeNone );
+ setShadowed( session->shadowed );
+ if( session->maximized != MaximizeRestore )
+ {
+ maximize( (MaximizeMode) session->maximized );
+ geom_restore = session->restore;
+ }
+ if( session->fullscreen == FullScreenHack )
+ ; // nothing, this should be already set again above
+ else if( session->fullscreen != FullScreenNone )
+ {
+ setFullScreen( true, false );
+ geom_fs_restore = session->fsrestore;
+ }
+ }
+ else
+ {
+ geom_restore = geometry(); // remember restore geometry
+ if ( isMaximizable()
+ && ( width() >= area.width() || height() >= area.height() ) )
+ {
+ // window is too large for the screen, maximize in the
+ // directions necessary
+ if ( width() >= area.width() && height() >= area.height() )
+ {
+ maximize( Client::MaximizeFull );
+ geom_restore = TQRect(); // use placement when unmaximizing
+ }
+ else if ( width() >= area.width() )
+ {
+ maximize( Client::MaximizeHorizontal );
+ geom_restore = TQRect(); // use placement when unmaximizing
+ geom_restore.setY( y()); // but only for horizontal direction
+ geom_restore.setHeight( height());
+ }
+ else if ( height() >= area.height() )
+ {
+ maximize( Client::MaximizeVertical );
+ geom_restore = TQRect(); // use placement when unmaximizing
+ geom_restore.setX( x()); // but only for vertical direction
+ geom_restore.setWidth( width());
+ }
+ }
+ // window may want to be maximized
+ // done after checking that the window isn't larger than the workarea, so that
+ // the restore geometry from the checks above takes precedence, and window
+ // isn't restored larger than the workarea
+ MaximizeMode maxmode = static_cast< MaximizeMode >
+ ((( info->state() & NET::MaxVert ) ? MaximizeVertical : 0 )
+ | (( info->state() & NET::MaxHoriz ) ? MaximizeHorizontal : 0 ));
+ MaximizeMode forced_maxmode = rules()->checkMaximize( maxmode, !isMapped );
+ // either hints were set to maximize, or is forced to maximize,
+ // or is forced to non-maximize and hints were set to maximize
+ if( forced_maxmode != MaximizeRestore || maxmode != MaximizeRestore )
+ maximize( forced_maxmode );
+
+ // read other initial states
+ setShade( rules()->checkShade( info->state() & NET::Shaded ? ShadeNormal : ShadeNone, !isMapped ));
+ setKeepAbove( rules()->checkKeepAbove( info->state() & NET::KeepAbove, !isMapped ));
+ setKeepBelow( rules()->checkKeepBelow( info->state() & NET::KeepBelow, !isMapped ));
+ setSkipTaskbar( rules()->checkSkipTaskbar( info->state() & NET::SkipTaskbar, !isMapped ), true );
+ setSkipPager( rules()->checkSkipPager( info->state() & NET::SkipPager, !isMapped ));
+ if( info->state() & NET::DemandsAttention )
+ demandAttention();
+ if( info->state() & NET::Modal )
+ setModal( true );
+ if( fullscreen_mode != FullScreenHack && isFullScreenable())
+ setFullScreen( rules()->checkFullScreen( info->state() & NET::FullScreen, !isMapped ), false );
+ }
+
+ updateAllowedActions( true );
+
+ // TODO this should avoid flicker, because real restacking is done
+ // only after manage() finishes, but the window is shown sooner
+ // - keep it?
+ XLowerWindow( tqt_xdisplay(), frameId());
+
+ // set initial user time directly
+ user_time = readUserTimeMapTimestamp( asn_valid ? &asn_id : NULL, asn_valid ? &asn_data : NULL, session );
+ group()->updateUserTime( user_time ); // and do what Client::updateUserTime() does
+
+ if( isTopMenu()) // they're shown in Workspace::addClient() if their mainwindow
+ hideClient( true ); // is the active one
+
+ if( isShown( true ) && !doNotShow )
+ {
+ if( isDialog())
+ Notify::raise( Notify::TransNew );
+ if( isNormalWindow())
+ Notify::raise( Notify::New );
+
+ bool allow;
+ if( session )
+ allow = session->active
+ && ( !workspace()->wasUserInteraction()
+ || workspace()->activeClient() == NULL || workspace()->activeClient()->isDesktop());
+ else
+ allow = workspace()->allowClientActivation( this, userTime(), false );
+
+ // if session saving, force showing new windows (i.e. "save file?" dialogs etc.)
+ // also force if activation is allowed
+ if( !isOnCurrentDesktop() && !isMapped && !session && ( allow || workspace()->sessionSaving()))
+ workspace()->setCurrentDesktop( desktop());
+
+ bool belongs_to_desktop = false;
+ for( ClientList::ConstIterator it = group()->members().begin();
+ it != group()->members().end();
+ ++it )
+ if( (*it)->isDesktop())
+ {
+ belongs_to_desktop = true;
+ break;
+ }
+ if( !belongs_to_desktop && workspace()->showingDesktop())
+ workspace()->resetShowingDesktop( options->showDesktopIsMinimizeAll );
+
+ if( isOnCurrentDesktop() && !isMapped && !allow )
+ workspace()->restackClientUnderActive( this );
+ else
+ workspace()->raiseClient( this );
+
+ updateVisibility();
+
+ if( !isMapped )
+ {
+ if( allow && isOnCurrentDesktop())
+ {
+ if( !isSpecialWindow())
+ if ( options->focusPolicyIsReasonable() && wantsTabFocus() )
+ workspace()->requestFocus( this );
+ }
+ else
+ {
+ if( !session && !isSpecialWindow())
+ demandAttention();
+ }
+ }
+ }
+ else if( !doNotShow ) // if( !isShown( true ) && !doNotShow )
+ {
+ updateVisibility();
+ }
+ else // doNotShow
+ { // SELI HACK !!!
+ hideClient( true );
+ setMappingState( IconicState );
+ }
+ assert( mappingState() != WithdrawnState );
+
+ if( user_time == CurrentTime || user_time == -1U ) // no known user time, set something old
+ {
+ user_time = GET_QT_X_TIME() - 1000000;
+ if( user_time == CurrentTime || user_time == -1U ) // let's be paranoid
+ user_time = GET_QT_X_TIME() - 1000000 + 10;
+ }
+
+ updateWorkareaDiffs();
+
+// sendSyntheticConfigureNotify(); done when setting mapping state
+
+ delete session;
+
+ ungrabXServer();
+
+ client_rules.discardTemporary();
+ applyWindowRules(); // just in case
+ workspace()->discardUsedWindowRules( this, false ); // remove ApplyNow rules
+ updateWindowRules(); // was blocked while !isManaged()
+
+// Handle suspended processes
+ if (isResumeable())
+ {
+ suspendWindow(); // It won't hurt to stop the process again, and this will update the displayed captions
+ }
+
+// TODO there's a small problem here - isManaged() depends on the mapping state,
+// but this client is not yet in Workspace's client list at this point, will
+// be only done in addClient()
+ return true;
+ }
+
+// called only from manage()
+void Client::embedClient( Window w, const XWindowAttributes &attr )
+ {
+ assert( client == None );
+ assert( frame == None );
+ assert( wrapper == None );
+ client = w;
+ // we don't want the window to be destroyed when we are destroyed
+ XAddToSaveSet( tqt_xdisplay(), client );
+ XSelectInput( tqt_xdisplay(), client, NoEventMask );
+ XUnmapWindow( tqt_xdisplay(), client );
+ XWindowChanges wc; // set the border width to 0
+ wc.border_width = 0; // TODO possibly save this, and also use it for initial configuring of the window
+ XConfigureWindow( tqt_xdisplay(), client, CWBorderWidth, &wc );
+
+ XSetWindowAttributes swa;
+ swa.colormap = attr.colormap;
+ swa.background_pixmap = None;
+ swa.border_pixel = 0;
+
+ frame = XCreateWindow( tqt_xdisplay(), tqt_xrootwin(), 0, 0, 1, 1, 0,
+ attr.depth, InputOutput, attr.visual,
+ CWColormap | CWBackPixmap | CWBorderPixel, &swa );
+ wrapper = XCreateWindow( tqt_xdisplay(), frame, 0, 0, 1, 1, 0,
+ attr.depth, InputOutput, attr.visual,
+ CWColormap | CWBackPixmap | CWBorderPixel, &swa );
+
+ XDefineCursor( tqt_xdisplay(), frame, tqarrowCursor.handle());
+ // some apps are stupid and don't define their own cursor - set the arrow one for them
+ XDefineCursor( tqt_xdisplay(), wrapper, tqarrowCursor.handle());
+ XReparentWindow( tqt_xdisplay(), client, wrapper, 0, 0 );
+ XSelectInput( tqt_xdisplay(), frame,
+ KeyPressMask | KeyReleaseMask |
+ ButtonPressMask | ButtonReleaseMask |
+ KeymapStateMask |
+ ButtonMotionMask |
+ PointerMotionMask |
+ EnterWindowMask | LeaveWindowMask |
+ FocusChangeMask |
+ ExposureMask |
+ PropertyChangeMask |
+ StructureNotifyMask | SubstructureRedirectMask );
+ XSelectInput( tqt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
+ XSelectInput( tqt_xdisplay(), client,
+ FocusChangeMask |
+ PropertyChangeMask |
+ ColormapChangeMask |
+ EnterWindowMask | LeaveWindowMask |
+ KeyPressMask | KeyReleaseMask
+ );
+ updateMouseGrab();
+ }
+
+} // namespace