diff --git a/client.cpp b/client.cpp index 68afab0cf4..e3a4f51590 100644 --- a/client.cpp +++ b/client.cpp @@ -28,6 +28,7 @@ License. See the file "COPYING" for the exact licensing terms. #include "atoms.h" #include "notifications.h" #include "rules.h" +#include "scene.h" #include #include @@ -63,12 +64,10 @@ namespace KWinInternal is done in manage(). */ Client::Client( Workspace *ws ) - : QObject( NULL ), + : Toplevel( ws ), client( None ), wrapper( None ), - frame( None ), decoration( NULL ), - wspace( ws ), bridge( new Bridge( this )), move_faked_activity( false ), move_resize_grab_window( None ), @@ -82,7 +81,7 @@ Client::Client( Workspace *ws ) process_killer( NULL ), user_time( CurrentTime ), // not known yet allowed_actions( 0 ), - postpone_geometry_updates( 0 ), + block_geometry_updates( 0 ), pending_geometry_update( false ), shade_geometry_change( false ), border_left( 0 ), @@ -111,7 +110,6 @@ 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; @@ -124,6 +122,7 @@ Client::Client( Workspace *ws ) modal = false; noborder = false; user_noborder = false; + not_obscured = false; urgency = false; ignore_focus_stealing = false; demands_attention = false; @@ -142,7 +141,7 @@ Client::Client( Workspace *ws ) cmap = None; - frame_geometry = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0) + geom = QRect( 0, 0, 100, 100 ); // so that decorations don't start with size being (0,0) client_size = QSize( 100, 100 ); // SELI initialize xsizehints?? @@ -155,9 +154,10 @@ Client::~Client() { assert(!moveResizeMode); assert( client == None ); - assert( frame == None && wrapper == None ); + assert( wrapper == None ); +// assert( frameId() == None ); assert( decoration == NULL ); - assert( postpone_geometry_updates == 0 ); + assert( block_geometry_updates == 0 ); assert( !check_active_modal ); delete info; delete bridge; @@ -176,15 +176,15 @@ void Client::releaseWindow( bool on_shutdown ) { assert( !deleting ); deleting = true; + finishCompositing(); workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules StackingUpdatesBlocker blocker( workspace()); if (moveResizeMode) leaveMoveResize(); finishWindowRules(); - ++postpone_geometry_updates; - // grab X during the release to make removing of properties, setting to withdrawn state - // and repareting to root an atomic operation (http://lists.kde.org/?l=kde-devel&m=116448102901184&w=2) - grabXServer(); + ++block_geometry_updates; + if( isNormalState()) // is mapped? + workspace()->addDamage( geometry()); setMappingState( WithdrawnState ); setModal( false ); // otherwise its mainwindow wouldn't get focus hidden = true; // so that it's not considered visible anymore (can't use hideClient(), it would set flags) @@ -222,12 +222,10 @@ void Client::releaseWindow( bool on_shutdown ) client = None; XDestroyWindow( display(), wrapper ); wrapper = None; - XDestroyWindow( display(), frame ); - frame = None; - --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry - checkNonExistentClients(); + XDestroyWindow( display(), frameId()); +// frame = None; + --block_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry deleteClient( this, Allowed ); - ungrabXServer(); } // like releaseWindow(), but this one is called when the window has been already destroyed @@ -236,12 +234,15 @@ void Client::destroyClient() { assert( !deleting ); deleting = true; + finishCompositing(); workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules StackingUpdatesBlocker blocker( workspace()); if (moveResizeMode) leaveMoveResize(); finishWindowRules(); - ++postpone_geometry_updates; + ++block_geometry_updates; + if( isNormalState()) // is mapped? + workspace()->addDamage( geometry()); setModal( false ); hidden = true; // so that it's not considered visible anymore workspace()->clientHidden( this ); @@ -251,10 +252,9 @@ void Client::destroyClient() client = None; // invalidate XDestroyWindow( display(), wrapper ); wrapper = None; - XDestroyWindow( display(), frame ); - frame = None; - --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry - checkNonExistentClients(); + XDestroyWindow( display(), frameId()); +// frame = None; + --block_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry deleteClient( this, Allowed ); } @@ -264,12 +264,11 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) || ( decoration != NULL && !noBorder()))) return; bool do_show = false; - postponeGeometryUpdates( true ); + blockGeometryUpdates( true ); if( force ) destroyDecoration(); if( !noBorder()) { - setMask( QRegion()); // reset shape mask decoration = workspace()->createDecoration( bridge ); // TODO check decoration's minimum size? decoration->init(); @@ -284,15 +283,20 @@ 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( compositing() ) + discardWindowPixmap(); + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); } else destroyDecoration(); if( check_workspace_pos ) checkWorkspacePosition(); - postponeGeometryUpdates( false ); + blockGeometryUpdates( false ); if( do_show ) decoration->widget()->show(); updateFrameExtents(); + addDamageFull(); } void Client::destroyDecoration() @@ -310,6 +314,11 @@ void Client::destroyDecoration() move( grav ); workarea_diff_x = save_workarea_diff_x; workarea_diff_y = save_workarea_diff_y; + if( compositing() ) + discardWindowPixmap(); + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); + addDamageFull(); } } @@ -322,7 +331,7 @@ void Client::checkBorderSizes() if( new_left == border_left && new_right == border_right && new_top == border_top && new_bottom == border_bottom ) return; - GeometryUpdatesPostponer blocker( this ); + GeometryUpdatesBlocker blocker( this ); move( calculateGravitation( true )); border_left = new_left; border_right = new_right; @@ -339,7 +348,7 @@ void Client::checkBorderSizes() void Client::detectNoBorder() { - if( Shape::hasShape( window())) + if( shape()) { noborder = true; return; @@ -427,47 +436,24 @@ 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( compositing() ) + discardWindowPixmap(); + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); + addDamageFull(); // 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 ) @@ -496,7 +482,11 @@ void Client::setMask( const QRegion& reg, int mode ) xrects, rects.count(), ShapeSet, mode ); delete[] xrects; } - updateShape(); + if( compositing() ) + discardWindowPixmap(); + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); + addDamageFull(); } QRegion Client::mask() const @@ -745,7 +735,7 @@ void Client::setShade( ShadeMode mode ) } assert( decoration != NULL ); // noborder windows can't be shaded - GeometryUpdatesPostponer blocker( this ); + GeometryUpdatesBlocker blocker( this ); // decorations may turn off some borders when shaded decoration->borders( border_left, border_right, border_top, border_bottom ); @@ -792,9 +782,13 @@ void Client::setShade( ShadeMode mode ) // tell xcompmgr shade's done _shade = 2; XChangeProperty(display(), frameId(), atoms->net_wm_window_shade, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &_shade, 1L); + if( isNormalState()) // is mapped? + workspace()->addDamage( geometry()); } else { + if( isNormalState()) // is mapped? + workspace()->addDamage( geometry()); int h = height(); shade_geometry_change = true; QSize s( sizeForClientSize( clientSize())); @@ -929,8 +923,8 @@ void Client::setMappingState(int s) XChangeProperty(display(), window(), atoms->wm_state, atoms->wm_state, 32, PropModeReplace, (unsigned char *)data, 2); - if( was_unmanaged ) // manage() did postpone_geometry_updates = 1, now it's ok to finally set the geometry - postponeGeometryUpdates( false ); + if( was_unmanaged ) // manage() did block_geometry_updates = 1, now it's ok to finally set the geometry + blockGeometryUpdates( false ); } /*! @@ -941,12 +935,17 @@ void Client::rawShow() { if( decoration != NULL ) decoration->widget()->show(); // not really necessary, but let it know the state - XMapWindow( display(), frame ); + XMapWindow( display(), frameId()); if( !isShade()) { XMapWindow( display(), wrapper ); XMapWindow( display(), client ); } + // XComposite invalidates backing pixmaps on unmap (minimize, different + // virtual desktop, etc.). We kept the last known good pixmap around + // for use in effects, but now we want to have access to the new pixmap + if( compositing() ) + discardWindowPixmap(); } /*! @@ -956,6 +955,7 @@ void Client::rawShow() */ void Client::rawHide() { + workspace()->addDamage( geometry()); // Here it may look like a race condition, as some other client might try to unmap // the window between these two XSelectInput() calls. However, they're supposed to // use XWithdrawWindow(), which also sends a synthetic event to the root window, @@ -963,7 +963,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(), frame ); + XUnmapWindow( display(), frameId()); XUnmapWindow( display(), wrapper ); XUnmapWindow( display(), client ); XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask ); @@ -1308,7 +1308,7 @@ QString Client::readName() const return KWin::readNameProperty( window(), XA_WM_NAME ); } -KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption()); +KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, Client, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption()); void Client::setCaption( const QString& _s, bool force ) { @@ -1625,58 +1625,6 @@ 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 ); @@ -1708,6 +1656,12 @@ 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 @@ -1804,46 +1758,24 @@ void Client::cancelAutoRaise() autoRaiseTimer = 0; } -#ifndef NDEBUG -kdbgstream& operator<<( kdbgstream& stream, const Client* cl ) +double Client::opacity() const { - if( cl == NULL ) - return stream << "\'NULL_CLIENT\'"; - return stream << "\'ID:" << cl->window() << ";WMCLASS:" << cl->resourceClass() << ":" << cl->resourceName() << ";Caption:" << cl->caption() << "\'"; + if( info->opacity() == 0xffffffff ) + return 1.0; + return info->opacity() * 1.0 / 0xffffffff; } -kdbgstream& operator<<( kdbgstream& stream, const ClientList& list ) + +void Client::setOpacity( double opacity ) { - 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; + opacity = qBound( 0.0, opacity, 1.0 ); + info->setOpacity( static_cast< unsigned long >( opacity * 0xffffffff )); + // we'll react on PropertyNotify } -kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& list ) + +void Client::debug( kdbgstream& stream ) const { - 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; + stream << "\'ID:" << window() << ";WMCLASS:" << resourceClass() << ":" << resourceName() << ";Caption:" << caption() << "\'"; } -#endif QPixmap * kwin_get_menu_pix_hack() { diff --git a/composite.cpp b/composite.cpp index c3c9a95a8f..168c139236 100644 --- a/composite.cpp +++ b/composite.cpp @@ -299,6 +299,14 @@ void Toplevel::finishCompositing() damage_region = QRegion(); } +void Toplevel::discardWindowPixmap() + { + if( window_pix == None ) + return; + XFreePixmap( display(), window_pix ); + window_pix = None; + } + Pixmap Toplevel::createWindowPixmap() const { assert( compositing()); diff --git a/events.cpp b/events.cpp index fece719bc4..949fb0d377 100644 --- a/events.cpp +++ b/events.cpp @@ -1615,6 +1615,8 @@ bool Unmanaged::windowEvent( XEvent* e ) { detectShape( handle()); addDamageFull(); + if( compositing() ) + discardWindowPixmap(); if( scene != NULL ) scene->windowGeometryShapeChanged( this ); } diff --git a/scene_basic.cpp b/scene_basic.cpp index 8e135ccc01..480958fba9 100644 --- a/scene_basic.cpp +++ b/scene_basic.cpp @@ -47,10 +47,9 @@ void SceneBasic::paint( QRegion, ToplevelList windows ) QRect r = (*it)->geometry().intersect( QRect( 0, 0, displayWidth(), displayHeight())); if( !r.isEmpty()) { - Pixmap pix = (*it)->createWindowPixmap(); + Pixmap pix = (*it)->windowPixmap(); XCopyArea( display(), pix, composite_pixmap, gc, qMax( 0, -(*it)->x()), qMax( 0, -(*it)->y()), r.width(), r.height(), r.x(), r.y()); - XFreePixmap( display(), pix ); } } XCopyArea( display(), composite_pixmap, rootWindow(), gc, 0, 0, displayWidth(), displayHeight(), 0, 0 ); diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 2d24653727..eab12c95cf 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -622,8 +622,7 @@ void SceneOpenGL::Window::bindTexture() return; } // Get the pixmap with the window contents - Pixmap window_pix = toplevel->createWindowPixmap(); - Pixmap pix = window_pix; + Pixmap pix = toplevel->windowPixmap(); // HACK // When a window uses ARGB visual and has a decoration, the decoration // does use ARGB visual. When converting such window to a texture @@ -698,21 +697,24 @@ void SceneOpenGL::Window::bindTexture() } XFreeGC( display(), gc ); } - // the pixmap is no longer needed, the texture will be updated - // only when the window changes anyway, so no need to cache - // the pixmap - XFreePixmap( display(), pix ); + // if using copy_buffer, the pixmap is no longer needed, the + // texture will be updated only when the window changes anyway, + // so no need to cache the pixmap + if( copy_buffer ) + XFreePixmap( display(), pix ); texture_y_inverted = true; toplevel->resetDamage( toplevel->rect()); } else if( tfp_mode ) { // tfp mode, simply bind the pixmap to texture + // TODO what should the lifetime of bound_pixmap be? if( texture == None ) glGenTextures( 1, &texture ); if( bound_pixmap != None && !strict_binding ) // release old if needed { glXReleaseTexImageEXT( display(), bound_glxpixmap, GLX_FRONT_LEFT_EXT ); glXDestroyGLXPixmap( display(), bound_glxpixmap ); + // TODO bound_pixmap shouldn't always be freed XFreePixmap( display(), bound_pixmap ); } static const int attrs[] = @@ -765,10 +767,11 @@ void SceneOpenGL::Window::bindTexture() } } glXWaitGL(); - // the pixmap is no longer needed, the texture will be updated - // only when the window changes anyway, so no need to cache - // the pixmap - XFreePixmap( display(), pix ); + // if using copy_buffer, the pixmap is no longer needed, the + // texture will be updated only when the window changes anyway, + // so no need to cache the pixmap + if( copy_buffer ) + XFreePixmap( display(), pix ); if( db ) glDrawBuffer( GL_BACK ); glXMakeContextCurrent( display(), glxbuffer, glxbuffer, ctxbuffer ); @@ -776,8 +779,6 @@ void SceneOpenGL::Window::bindTexture() texture_y_inverted = false; toplevel->resetDamage( toplevel->rect()); } - if( copy_buffer ) - XFreePixmap( display(), window_pix ); } @@ -839,7 +840,12 @@ void SceneOpenGL::Window::discardTexture() if( !strict_binding ) glXReleaseTexImageEXT( display(), bound_glxpixmap, GLX_FRONT_LEFT_EXT ); glXDestroyGLXPixmap( display(), bound_glxpixmap ); - XFreePixmap( display(), bound_pixmap ); + // TODO this is broken, and may need changing anyway depending on + // how tfp_mode deals with the new windowPixmap() + if( bound_pixmap != toplevel->windowPixmap( false ) ) + // if using copy_buffer, bound_pixmap is independent of + // windowPixmap(), so free it now + XFreePixmap( display(), bound_pixmap ); bound_pixmap = None; bound_glxpixmap = None; } diff --git a/scene_xrender.cpp b/scene_xrender.cpp index 7c171ee6cd..92b3d66068 100644 --- a/scene_xrender.cpp +++ b/scene_xrender.cpp @@ -305,8 +305,7 @@ Picture SceneXrender::Window::picture() if( _picture == None && format != NULL ) { // Get the pixmap with the window contents. - Pixmap window_pix = toplevel->createWindowPixmap(); - Pixmap pix = window_pix; + Pixmap pix = toplevel->windowPixmap(); // HACK the same alpha clear hack like with opengl, see there Client* c = dynamic_cast< Client* >( toplevel ); bool alpha_clear = c != NULL && c->hasAlpha() && !c->noBorder(); @@ -336,10 +335,9 @@ Picture SceneXrender::Window::picture() XFreeGC( display(), gc ); } _picture = XRenderCreatePicture( display(), pix, format, 0, 0 ); - XFreePixmap( display(), pix ); // the picture owns the pixmap #ifdef ALPHA_CLEAR_COPY if( alpha_clear ) - XFreePixmap( display(), window_pix ); + XFreePixmap( display(), pix ); #endif toplevel->resetDamage( toplevel->rect()); } diff --git a/toplevel.cpp b/toplevel.cpp index f9c1eac435..f1679b8199 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -17,6 +17,7 @@ Toplevel::Toplevel( Workspace* ws ) : vis( None ) , id( None ) , wspace( ws ) + , window_pix( None ) , damage_handle( None ) , is_shape( false ) { @@ -25,6 +26,7 @@ Toplevel::Toplevel( Workspace* ws ) Toplevel::~Toplevel() { assert( damage_handle == None ); + discardWindowPixmap(); } #ifndef NDEBUG diff --git a/toplevel.h b/toplevel.h index cf655320df..3fad3814d3 100644 --- a/toplevel.h +++ b/toplevel.h @@ -53,7 +53,7 @@ class Toplevel bool isSplash() const; bool isUtility() const; - Pixmap createWindowPixmap() const; + Pixmap windowPixmap( bool allow_create = true ); // for use with compositing Visual* visual() const; bool shape() const; virtual double opacity() const = 0; @@ -70,6 +70,8 @@ class Toplevel void setHandle( Window id ); void detectShape( Window id ); void damageNotifyEvent( XDamageNotifyEvent* e ); + Pixmap createWindowPixmap() const; + void discardWindowPixmap(); QRect geom; Visual* vis; int bit_depth; @@ -78,6 +80,7 @@ class Toplevel private: Window id; Workspace* wspace; + Pixmap window_pix; Damage damage_handle; QRegion damage_region; bool is_shape; @@ -189,6 +192,13 @@ inline bool Toplevel::isNormalWindow() const return windowType() == NET::Normal; } +inline Pixmap Toplevel::windowPixmap( bool allow_create ) + { + if( window_pix == None && allow_create ) + window_pix = createWindowPixmap(); + return window_pix; + } + inline QRegion Toplevel::damage() const { return damage_region;