You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
536 lines
18 KiB
536 lines
18 KiB
/***************************************************************** |
|
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 <kstartupinfo.h> |
|
#include <X11/extensions/shape.h> |
|
|
|
#include "notifications.h" |
|
|
|
extern Time qt_x_time; |
|
extern Atom qt_window_role; |
|
|
|
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(qt_xdisplay(), w, &attr)) |
|
return false; |
|
|
|
// initial state |
|
int init_mapping_state = NormalState; |
|
XWMHints * hints = XGetWMHints(qt_xdisplay(), w ); |
|
if (hints && (hints->flags & StateHint)) |
|
init_mapping_state = hints->initial_state; |
|
if (hints) |
|
XFree(hints); |
|
if( isMapped ) |
|
init_mapping_state = NormalState; // if it's already mapped, ignore hint |
|
|
|
if( init_mapping_state != NormalState |
|
&& init_mapping_state != IconicState ) |
|
{ // don't manage windows with strange initial mapping state |
|
// it's mapped and unmapped for WindowMaker applets |
|
// they usually map with initial_state == Withdrawn, |
|
// and don't want to be mapped |
|
// mapping it for a while will give the docking panel |
|
// a chance to get MapNotify for it, and swallow the matching |
|
// window |
|
// SELI |
|
XMapWindow( qt_xdisplay(), w ); |
|
XUnmapWindow( qt_xdisplay(), w ); |
|
return false; |
|
} |
|
|
|
// XGrabServer( qt_xdisplay()); // FRAME |
|
|
|
// from this place on, manage() mustn't return false |
|
block_geometry = 1; |
|
|
|
embedClient( w ); |
|
|
|
// SELI order all these things in some sane manner |
|
|
|
bool init_minimize = init_mapping_state == IconicState; |
|
|
|
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 | |
|
0; |
|
|
|
info = new WinInfo( this, qt_xdisplay(), client, qt_xrootwin(), properties, 2 ); |
|
|
|
cmap = attr.colormap; |
|
|
|
bool mresize, mmove, mminimize, mmaximize, mclose; |
|
if( Motif::funcFlags( client, mresize, mmove, mminimize, mmaximize, mclose )) |
|
{ |
|
motif_may_resize = mresize; // this should be set using minsize==maxsize, but oh well |
|
motif_may_move = mmove; |
|
// mminimize; - ignore, bogus - e.g. shading or sending to another desktop is "minimizing" too |
|
// mmaximize; - ignore, bogus - maximizing is basically just resizing |
|
motif_may_close = mclose; // motif apps like to crash when they set this hint and WM closes them anyway |
|
} |
|
|
|
XClassHint classHint; |
|
if ( XGetClassHint( qt_xdisplay(), client, &classHint ) ) |
|
{ |
|
resource_name = classHint.res_name; |
|
resource_class = classHint.res_class; |
|
XFree( classHint.res_name ); |
|
XFree( classHint.res_class ); |
|
} |
|
|
|
detectNoBorder(); |
|
fetchName(); |
|
fetchIconicName(); |
|
getWMHints(); |
|
readTransient(); |
|
getIcons(); |
|
getWindowProtocols(); |
|
getWmNormalHints(); // get xSizeHint |
|
getWmClientLeader(); |
|
window_role = getStringProperty( w, qt_window_role ); |
|
|
|
// 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; |
|
modal = ( info->state() & NET::Modal ) != 0; |
|
|
|
// window wants to stay on top? |
|
keep_above = ( info->state() & NET::KeepAbove ) != 0; |
|
// window wants to stay on bottom? |
|
keep_below = ( info->state() & NET::KeepBelow ) != 0; |
|
if( keep_above && keep_below ) |
|
keep_above = keep_below = false; |
|
|
|
KStartupInfoData asn_data; |
|
bool asn_valid = workspace()->checkStartupNotification( this, asn_data ); |
|
|
|
workspace()->updateClientLayer( this ); |
|
|
|
SessionInfo* session = workspace()->takeSessionInfo( this ); |
|
if ( session ) |
|
{ |
|
if ( session->minimized ) |
|
init_minimize = true; |
|
if( session->userNoBorder ) |
|
setUserNoBorder( true ); |
|
} |
|
|
|
// initial desktop placement |
|
if ( info->desktop() ) |
|
desk = info->desktop(); // window had the initial desktop property! |
|
else if( asn_valid && asn_data.desktop() != 0 ) |
|
desk = asn_data.desktop(); |
|
if ( session ) |
|
{ |
|
desk = session->desktop; |
|
if( session->onAllDesktops ) |
|
desk = NET::OnAllDesktops; |
|
} |
|
else if ( desk == 0 ) |
|
{ |
|
// 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; |
|
for( ClientList::ConstIterator it = mainclients.begin(); |
|
it != mainclients.end(); |
|
++it ) |
|
if( (*it)->isOnCurrentDesktop()) |
|
on_current = true; |
|
if( on_current ) |
|
desk = workspace()->currentDesktop(); |
|
else if( mainclients.count() > 0 ) |
|
desk = mainclients.first()->desktop(); |
|
} |
|
} |
|
if ( desk == 0 ) // assume window wants to be visible on the current desktop |
|
desk = workspace()->currentDesktop(); |
|
info->setDesktop( desk ); |
|
workspace()->updateOnAllDesktopsOfTransients( this ); // SELI |
|
// onAllDesktopsChange(); decoration doesn't exist here yet |
|
|
|
QRect geom( attr.x, attr.y, attr.width, attr.height ); |
|
bool placementDone = FALSE; |
|
|
|
if ( session ) |
|
geom = session->geometry; |
|
|
|
QRect area = workspace()->clientArea( geom.center(), desktop()); |
|
|
|
|
|
if (( geom.size() == workspace()->geometry().size()) // XINERAMA TODO check also size of the screen |
|
&& noBorder() && !isUserNoBorder() && isFullScreenable()) |
|
{ |
|
fullscreen_mode = FullScreenHack; |
|
// TODO XINERAMA show fullscreen on only one xinerama screen |
|
geom.moveTopLeft( QPoint( 0, 0 )); // in case they try to make it fullscreen, but misplace (#55290) |
|
placementDone = true; |
|
} |
|
|
|
if ( isDesktop() ) |
|
{ |
|
// desktops are treated slightly special |
|
geom = workspace()->geometry(); |
|
placementDone = true; |
|
} |
|
|
|
if ( isMapped || session || placementDone |
|
|| ( isTransient() && !isUtility() && !isDialog() && !isSplash())) |
|
{ // TODO |
|
placementDone = TRUE; |
|
} |
|
else if( isTransient() && !hasNETSupport()) |
|
placementDone = true; |
|
else if( isDialog() && hasNETSupport()) // see Placement::placeDialog() |
|
; // force using placement policy |
|
else if( isSplash()) |
|
; // force using placement policy |
|
else |
|
{ |
|
|
|
bool ignorePPosition = false; |
|
XClassHint classHint; // APICLEAN tohle je zduplikovane seshora |
|
if ( XGetClassHint(qt_xdisplay(), window(), &classHint) != 0 ) |
|
{ |
|
if ( classHint.res_class ) |
|
ignorePPosition = ( options->ignorePositionClasses.find(QString::fromLatin1(classHint.res_class)) != options->ignorePositionClasses.end() ); |
|
XFree(classHint.res_name); |
|
XFree(classHint.res_class); |
|
} |
|
|
|
if ((xSizeHint.flags & PPosition) && ! ignorePPosition) |
|
{ |
|
int tx = geom.x(); |
|
int ty = geom.y(); |
|
|
|
// TODO tyhle testy nepocitaji s dekoraci, ani s gravity |
|
if (tx < 0) |
|
tx = area.right() + tx; |
|
if (ty < 0) |
|
ty = area.bottom() + ty; |
|
geom.moveTopLeft(QPoint(tx, ty)); |
|
} |
|
|
|
if ( ( (xSizeHint.flags & PPosition) && !ignorePPosition ) || |
|
(xSizeHint.flags & USPosition) ) |
|
{ |
|
placementDone = TRUE; |
|
} |
|
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( QSize(xSizeHint.max_width, xSizeHint.max_height ) ) ); |
|
if (xSizeHint.flags & PMinSize) |
|
geom.setSize( geom.size().expandedTo( QSize(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( sizeForClientSize( geom.size())); |
|
|
|
if( !placementDone ) |
|
{ // placement needs to be after setting size |
|
workspace()->place( this ); |
|
placementDone = TRUE; |
|
} |
|
|
|
if (( !isSpecialWindow() || isToolbar()) && isMovable()) |
|
{ |
|
if ( geometry().right() > area.right() && width() < area.width() ) |
|
move( area.right() - width(), y() ); |
|
if ( geometry().bottom() > area.bottom() && height() < area.height() ) |
|
move( x(), area.bottom() - height() ); |
|
if( !area.contains( geometry().topLeft() )) |
|
{ |
|
int tx = x(); |
|
int ty = y(); |
|
if ( tx < area.x() ) |
|
tx = area.x(); |
|
if ( ty < area.y() ) |
|
ty = area.y(); |
|
move( tx, ty ); |
|
} |
|
} |
|
|
|
XShapeSelectInput( qt_xdisplay(), window(), ShapeNotifyMask ); |
|
if ( (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()) |
|
init_minimize = false; // SELI even e.g. for NET::Utility? |
|
} |
|
|
|
if( init_minimize ) |
|
minimize(); |
|
|
|
// 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; |
|
|
|
if( isTopMenu()) |
|
doNotShow = true; |
|
|
|
// other settings from the previous session |
|
if ( session ) |
|
{ |
|
setKeepAbove( session->keepAbove ); |
|
setKeepBelow( session->keepBelow ); |
|
setSkipTaskbar( session->skipTaskbar, true ); |
|
setSkipPager( session->skipPager ); |
|
setShade( session->shaded ? ShadeNormal : ShadeNone ); |
|
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 = QRect(); // use placement when unmaximizing |
|
} |
|
else if ( width() >= area.width() ) |
|
{ |
|
maximize( Client::MaximizeHorizontal ); |
|
geom_restore = QRect(); // 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 = QRect(); // 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 |
|
if ( (info->state() & NET::Max) == NET::Max ) |
|
maximize( Client::MaximizeFull ); |
|
else if ( info->state() & NET::MaxVert ) |
|
maximize( Client::MaximizeVertical ); |
|
else if ( info->state() & NET::MaxHoriz ) |
|
maximize( Client::MaximizeHorizontal ); |
|
|
|
if( fullscreen_mode != FullScreenHack ) |
|
{ |
|
if(( info->state() & NET::FullScreen ) != 0 && isFullScreenable()) |
|
setFullScreen( true, 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( qt_xdisplay(), frameId()); |
|
|
|
user_time = readUserTimeMapTimestamp( asn_valid ? &asn_data : NULL, session ); |
|
|
|
if ( isShown() && !doNotShow ) |
|
{ |
|
if( isDialog()) |
|
Notify::raise( Notify::TransNew ); |
|
if( isNormalWindow()) |
|
Notify::raise( Notify::New ); |
|
|
|
// if session saving, force showing new windows (i.e. "save file?" dialogs etc.) |
|
if( workspace()->sessionSaving() && !isOnCurrentDesktop()) |
|
workspace()->setCurrentDesktop( desktop()); |
|
|
|
if( isOnCurrentDesktop()) |
|
{ |
|
setMappingState( NormalState ); |
|
|
|
if( isMapped ) |
|
{ |
|
workspace()->raiseClient( this ); |
|
rawShow(); |
|
} |
|
else |
|
{ |
|
if( workspace()->allowClientActivation( this, user_time, false, session && session->active )) |
|
{ |
|
workspace()->raiseClient( this ); |
|
rawShow(); |
|
if( !isSpecialWindow() || isOverride()) |
|
if ( options->focusPolicyIsReasonable() && wantsTabFocus() ) |
|
workspace()->requestFocus( this ); |
|
} |
|
else |
|
{ |
|
workspace()->restackClientUnderActive( this ); |
|
rawShow(); |
|
if( !session && ( !isSpecialWindow() || isOverride())) |
|
demandAttention(); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
virtualDesktopChange(); |
|
workspace()->raiseClient( this ); |
|
if( !session && !isMapped ) |
|
demandAttention(); |
|
} |
|
} |
|
else if( !doNotShow ) // !isShown() |
|
{ |
|
rawHide(); |
|
setMappingState( IconicState ); |
|
} |
|
else // doNotShow |
|
{ // SELI HACK !!! |
|
rawHide(); |
|
setMappingState( IconicState ); |
|
} |
|
assert( mappingState() != WithdrawnState ); |
|
|
|
if( isTopMenu()) |
|
hideClient( true ); |
|
|
|
if( user_time == CurrentTime ) // no known user time, set something old |
|
user_time = qt_x_time - 1000000; |
|
|
|
updateWorkareaDiffs( area ); |
|
|
|
// sendSyntheticConfigureNotify(); done when setting mapping state |
|
|
|
delete session; |
|
|
|
// XUngrabServer( qt_xdisplay()); FRAME |
|
|
|
return true; |
|
} |
|
|
|
// called only from manage() |
|
void Client::embedClient( Window w ) |
|
{ |
|
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( qt_xdisplay(), client ); |
|
XSelectInput( qt_xdisplay(), client, NoEventMask ); |
|
XUnmapWindow( qt_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( qt_xdisplay(), client, CWBorderWidth, &wc ); |
|
frame = XCreateSimpleWindow( qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0, 0, 0 ); |
|
wrapper = XCreateSimpleWindow( qt_xdisplay(), frame, 0, 0, 1, 1, 0, 0, 0 ); |
|
XDefineCursor( qt_xdisplay(), frame, arrowCursor.handle()); |
|
// some apps are stupid and don't define their own cursor - set the arrow one for them |
|
XDefineCursor( qt_xdisplay(), wrapper, arrowCursor.handle()); |
|
XReparentWindow( qt_xdisplay(), client, wrapper, 0, 0 ); |
|
XSelectInput( qt_xdisplay(), frame, |
|
KeyPressMask | KeyReleaseMask | |
|
ButtonPressMask | ButtonReleaseMask | |
|
KeymapStateMask | |
|
ButtonMotionMask | |
|
PointerMotionMask | |
|
EnterWindowMask | LeaveWindowMask | |
|
FocusChangeMask | |
|
ExposureMask | |
|
PropertyChangeMask | |
|
StructureNotifyMask | SubstructureRedirectMask | |
|
VisibilityChangeMask ); |
|
XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask ); |
|
XSelectInput( qt_xdisplay(), client, |
|
FocusChangeMask | |
|
PropertyChangeMask | |
|
ColormapChangeMask | |
|
EnterWindowMask | LeaveWindowMask | |
|
KeyPressMask | KeyReleaseMask |
|
); |
|
XSetWindowBackgroundPixmap( qt_xdisplay(), frameId(), None ); |
|
XSetWindowBackgroundPixmap( qt_xdisplay(), wrapperId(), None ); |
|
updateMouseGrab(); |
|
} |
|
|
|
} // namespace
|
|
|