diff --git a/client.cpp b/client.cpp index b24a72d72c..506028a119 100644 --- a/client.cpp +++ b/client.cpp @@ -28,7 +28,6 @@ License. See the file "COPYING" for the exact licensing terms. #include "atoms.h" #include "notifications.h" #include "rules.h" -#include "scene.h" #include #include @@ -64,10 +63,12 @@ namespace KWinInternal is done in manage(). */ Client::Client( Workspace *ws ) - : Toplevel( ws ), + : QObject( NULL ), client( None ), wrapper( None ), + frame( None ), decoration( NULL ), + wspace( ws ), bridge( new Bridge( this )), move_faked_activity( false ), move_resize_grab_window( None ), @@ -110,6 +111,7 @@ Client::Client( Workspace *ws ) deleting = false; keep_above = false; keep_below = false; + is_shape = false; motif_noborder = false; motif_may_move = true; motif_may_resize = true; @@ -141,7 +143,7 @@ Client::Client( Workspace *ws ) cmap = None; - geom = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0) + frame_geometry = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0) client_size = QSize( 100, 100 ); // SELI initialize xsizehints?? @@ -154,8 +156,7 @@ Client::~Client() { assert(!moveResizeMode); assert( client == None ); - assert( wrapper == None ); -// assert( frameId() == None ); + assert( frame == None && wrapper == None ); assert( decoration == NULL ); assert( postpone_geometry_updates == 0 ); assert( !check_active_modal ); @@ -176,7 +177,6 @@ void Client::releaseWindow( bool on_shutdown ) { assert( !deleting ); deleting = true; - finishCompositing(); workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules StackingUpdatesBlocker blocker( workspace()); if (moveResizeMode) @@ -220,8 +220,8 @@ void Client::releaseWindow( bool on_shutdown ) client = None; XDestroyWindow( display(), wrapper ); wrapper = None; - XDestroyWindow( display(), frameId()); -// frame = None; + XDestroyWindow( display(), frame ); + frame = None; --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry deleteClient( this, Allowed ); } @@ -232,7 +232,6 @@ void Client::destroyClient() { assert( !deleting ); deleting = true; - finishCompositing(); workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules StackingUpdatesBlocker blocker( workspace()); if (moveResizeMode) @@ -248,8 +247,8 @@ void Client::destroyClient() client = None; // invalidate XDestroyWindow( display(), wrapper ); wrapper = None; - XDestroyWindow( display(), frameId()); -// frame = None; + XDestroyWindow( display(), frame ); + frame = None; --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry deleteClient( this, Allowed ); } @@ -265,6 +264,7 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) destroyDecoration(); if( !noBorder()) { + setMask( QRegion()); // reset shape mask decoration = workspace()->createDecoration( bridge ); // TODO check decoration's minimum size? decoration->init(); @@ -279,8 +279,6 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) workarea_diff_x = save_workarea_diff_x; workarea_diff_y = save_workarea_diff_y; do_show = true; - if( scene != NULL ) - scene->windowGeometryShapeChanged( this ); } else destroyDecoration(); @@ -290,7 +288,6 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) if( do_show ) decoration->widget()->show(); updateFrameExtents(); - addDamage( rect()); } void Client::destroyDecoration() @@ -308,9 +305,6 @@ void Client::destroyDecoration() move( grav ); workarea_diff_x = save_workarea_diff_x; workarea_diff_y = save_workarea_diff_y; - if( scene != NULL ) - scene->windowGeometryShapeChanged( this ); - addDamage( rect()); } } @@ -340,7 +334,7 @@ void Client::checkBorderSizes() void Client::detectNoBorder() { - if( shape()) + if( Shape::hasShape( window())) { noborder = true; return; @@ -428,22 +422,47 @@ void Client::setUserNoBorder( bool set ) void Client::updateShape() { - if ( shape() ) - XShapeCombineShape(display(), frameId(), ShapeBounding, - clientPos().x(), clientPos().y(), - window(), ShapeBounding, ShapeSet); - else - XShapeCombineMask( display(), frameId(), ShapeBounding, 0, 0, - None, ShapeSet); - if( scene != NULL ) - scene->windowGeometryShapeChanged( this ); - addDamage( rect()); // workaround for #19644 - shaped windows shouldn't have decoration if( shape() && !noBorder()) { noborder = true; updateDecoration( true ); } + if( shape()) + { + XShapeCombineShape(display(), frameId(), ShapeBounding, + clientPos().x(), clientPos().y(), + window(), ShapeBounding, ShapeSet); + } + // !shape() mask setting is done in setMask() when the decoration + // calls it or when the decoration is created/destroyed + + if( Shape::version() >= 0x11 ) // 1.1, has input shape support + { // There appears to be no way to find out if a window has input + // shape set or not, so always propagate the input shape + // (it's the same like the bounding shape by default). + // Also, build the shape using a helper window, not directly + // in the frame window, because the sequence set-shape-to-frame, + // remove-shape-of-client, add-input-shape-of-client has the problem + // that after the second step there's a hole in the input shape + // until the real shape of the client is added and that can make + // the window lose focus (which is a problem with mouse focus policies) + static Window helper_window = None; + if( helper_window == None ) + helper_window = XCreateSimpleWindow( display(), rootWindow(), + 0, 0, 1, 1, 0, 0, 0 ); + XResizeWindow( display(), helper_window, width(), height()); + XShapeCombineShape( display(), helper_window, ShapeInput, 0, 0, + frameId(), ShapeBounding, ShapeSet ); + XShapeCombineShape( display(), helper_window, ShapeInput, + clientPos().x(), clientPos().y(), + window(), ShapeBounding, ShapeSubtract ); + XShapeCombineShape( display(), helper_window, ShapeInput, + clientPos().x(), clientPos().y(), + window(), ShapeInput, ShapeUnion ); + XShapeCombineShape( display(), frameId(), ShapeInput, 0, 0, + helper_window, ShapeInput, ShapeSet ); + } } void Client::setMask( const QRegion& reg, int mode ) @@ -472,9 +491,7 @@ void Client::setMask( const QRegion& reg, int mode ) xrects, rects.count(), ShapeSet, mode ); delete[] xrects; } - if( scene != NULL ) - scene->windowGeometryShapeChanged( this ); - addDamage( rect()); + updateShape(); } QRegion Client::mask() const @@ -919,7 +936,7 @@ void Client::rawShow() { if( decoration != NULL ) decoration->widget()->show(); // not really necessary, but let it know the state - XMapWindow( display(), frameId()); + XMapWindow( display(), frame ); if( !isShade()) { XMapWindow( display(), wrapper ); @@ -941,7 +958,7 @@ void Client::rawHide() // will be missed is also very minimal, so I don't think it's needed to grab the server // here. XSelectInput( display(), wrapper, ClientWinMask ); // avoid getting UnmapNotify - XUnmapWindow( display(), frameId()); + XUnmapWindow( display(), frame ); XUnmapWindow( display(), wrapper ); XUnmapWindow( display(), client ); XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask ); @@ -1286,7 +1303,7 @@ QString Client::readName() const return KWin::readNameProperty( window(), XA_WM_NAME ); } -KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, Client, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption()); +KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption()); void Client::setCaption( const QString& _s, bool force ) { @@ -1603,6 +1620,58 @@ bool Client::wantsInput() const return rules()->checkAcceptFocus( input || Ptakefocus ); } +bool Client::isDesktop() const + { + return windowType() == NET::Desktop; + } + +bool Client::isDock() const + { + return windowType() == NET::Dock; + } + +bool Client::isTopMenu() const + { + return windowType() == NET::TopMenu; + } + + +bool Client::isMenu() const + { + return windowType() == NET::Menu && !isTopMenu(); // because of backwards comp. + } + +bool Client::isToolbar() const + { + return windowType() == NET::Toolbar; + } + +bool Client::isSplash() const + { + return windowType() == NET::Splash; + } + +bool Client::isUtility() const + { + return windowType() == NET::Utility; + } + +bool Client::isDialog() const + { + return windowType() == NET::Dialog; + } + +bool Client::isNormalWindow() const + { + return windowType() == NET::Normal; + } + +bool Client::isSpecialWindow() const + { + return isDesktop() || isDock() || isSplash() || isTopMenu() + || isToolbar(); // TODO + } + NET::WindowType Client::windowType( bool direct, int supported_types ) const { NET::WindowType wt = info->windowType( supported_types ); @@ -1634,12 +1703,6 @@ NET::WindowType Client::windowType( bool direct, int supported_types ) const return wt; } -bool Client::isSpecialWindow() const - { - return isDesktop() || isDock() || isSplash() || isTopMenu() - || isToolbar(); // TODO - } - /*! Sets an appropriate cursor shape for the logical mouse position \a m @@ -1736,24 +1799,46 @@ void Client::cancelAutoRaise() autoRaiseTimer = 0; } -double Client::opacity() const +#ifndef NDEBUG +kdbgstream& operator<<( kdbgstream& stream, const Client* cl ) { - if( info->opacity() == 0xffffffff ) - return 1.0; - return info->opacity() * 1.0 / 0xffffffff; + if( cl == NULL ) + return stream << "\'NULL_CLIENT\'"; + return stream << "\'ID:" << cl->window() << ";WMCLASS:" << cl->resourceClass() << ":" << cl->resourceName() << ";Caption:" << cl->caption() << "\'"; } - -void Client::setOpacity( double opacity ) +kdbgstream& operator<<( kdbgstream& stream, const ClientList& list ) { - opacity = qBound( 0.0, opacity, 1.0 ); - info->setOpacity( static_cast< unsigned long >( opacity * 0xffffffff )); - // we'll react on PropertyNotify + stream << "LIST:("; + bool first = true; + for( ClientList::ConstIterator it = list.begin(); + it != list.end(); + ++it ) + { + if( !first ) + stream << ":"; + first = false; + stream << *it; + } + stream << ")"; + return stream; } - -void Client::debug( kdbgstream& stream ) const +kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& list ) { - stream << "\'ID:" << window() << ";WMCLASS:" << resourceClass() << ":" << resourceName() << ";Caption:" << caption() << "\'"; + stream << "LIST:("; + bool first = true; + for( ConstClientList::ConstIterator it = list.begin(); + it != list.end(); + ++it ) + { + if( !first ) + stream << ":"; + first = false; + stream << *it; + } + stream << ")"; + return stream; } +#endif QPixmap * kwin_get_menu_pix_hack() {