From 0cbf093fddbe370ffca7fa65ed04a53d51c7785a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= Date: Thu, 14 Dec 2006 16:32:55 +0000 Subject: [PATCH] Check for references to no longer existing windows in transiency checks only when the list of windows is in consistent state. svn path=/trunk/KDE/kdebase/workspace/; revision=613681 --- client.cpp | 229 ++++++++++++++++++++++++++++++++++---------------- group.cpp | 26 +++++- utils.h | 103 +++++------------------ workspace.cpp | 1 + 4 files changed, 199 insertions(+), 160 deletions(-) diff --git a/client.cpp b/client.cpp index d3127ece78..b2a9311556 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 ), @@ -81,7 +82,7 @@ Client::Client( Workspace *ws ) process_killer( NULL ), user_time( CurrentTime ), // not known yet allowed_actions( 0 ), - block_geometry_updates( 0 ), + postpone_geometry_updates( 0 ), pending_geometry_update( false ), shade_geometry_change( false ), border_left( 0 ), @@ -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; @@ -122,7 +124,6 @@ Client::Client( Workspace *ws ) modal = false; noborder = false; user_noborder = false; - not_obscured = false; urgency = false; ignore_focus_stealing = false; demands_attention = false; @@ -141,7 +142,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,10 +155,9 @@ Client::~Client() { assert(!moveResizeMode); assert( client == None ); - assert( wrapper == None ); -// assert( frameId() == None ); + assert( frame == None && wrapper == None ); assert( decoration == NULL ); - assert( block_geometry_updates == 0 ); + assert( postpone_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(); - ++block_geometry_updates; - if( isNormalState()) // is mapped? - workspace()->addDamage( geometry()); + ++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(); 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,10 +222,12 @@ void Client::releaseWindow( bool on_shutdown ) client = None; XDestroyWindow( display(), wrapper ); wrapper = None; - XDestroyWindow( display(), frameId()); -// frame = None; - --block_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry + XDestroyWindow( display(), frame ); + frame = None; + --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry deleteClient( this, Allowed ); + ungrabXServer(); + checkNonExistentClients(); } // like releaseWindow(), but this one is called when the window has been already destroyed @@ -234,15 +236,12 @@ void Client::destroyClient() { assert( !deleting ); deleting = true; - finishCompositing(); workspace()->discardUsedWindowRules( this, true ); // remove ForceTemporarily rules StackingUpdatesBlocker blocker( workspace()); if (moveResizeMode) leaveMoveResize(); finishWindowRules(); - ++block_geometry_updates; - if( isNormalState()) // is mapped? - workspace()->addDamage( geometry()); + ++postpone_geometry_updates; setModal( false ); hidden = true; // so that it's not considered visible anymore workspace()->clientHidden( this ); @@ -252,10 +251,11 @@ void Client::destroyClient() client = None; // invalidate XDestroyWindow( display(), wrapper ); wrapper = None; - XDestroyWindow( display(), frameId()); -// frame = None; - --block_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry + XDestroyWindow( display(), frame ); + frame = None; + --postpone_geometry_updates; // don't use GeometryUpdatesBlocker, it would now set the geometry deleteClient( this, Allowed ); + checkNonExistentClients(); } void Client::updateDecoration( bool check_workspace_pos, bool force ) @@ -264,11 +264,12 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) || ( decoration != NULL && !noBorder()))) return; bool do_show = false; - blockGeometryUpdates( true ); + postponeGeometryUpdates( true ); if( force ) destroyDecoration(); if( !noBorder()) { + setMask( QRegion()); // reset shape mask decoration = workspace()->createDecoration( bridge ); // TODO check decoration's minimum size? decoration->init(); @@ -283,18 +284,15 @@ 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(); if( check_workspace_pos ) checkWorkspacePosition(); - blockGeometryUpdates( false ); + postponeGeometryUpdates( false ); if( do_show ) decoration->widget()->show(); updateFrameExtents(); - addDamageFull(); } void Client::destroyDecoration() @@ -312,9 +310,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 ); - addDamageFull(); } } @@ -327,7 +322,7 @@ void Client::checkBorderSizes() if( new_left == border_left && new_right == border_right && new_top == border_top && new_bottom == border_bottom ) return; - GeometryUpdatesBlocker blocker( this ); + GeometryUpdatesPostponer blocker( this ); move( calculateGravitation( true )); border_left = new_left; border_right = new_right; @@ -344,7 +339,7 @@ void Client::checkBorderSizes() void Client::detectNoBorder() { - if( shape()) + if( Shape::hasShape( window())) { noborder = true; return; @@ -432,22 +427,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 ); - 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 ) @@ -476,9 +496,7 @@ void Client::setMask( const QRegion& reg, int mode ) xrects, rects.count(), ShapeSet, mode ); delete[] xrects; } - if( scene != NULL ) - scene->windowGeometryShapeChanged( this ); - addDamageFull(); + updateShape(); } QRegion Client::mask() const @@ -727,7 +745,7 @@ void Client::setShade( ShadeMode mode ) } assert( decoration != NULL ); // noborder windows can't be shaded - GeometryUpdatesBlocker blocker( this ); + GeometryUpdatesPostponer blocker( this ); // decorations may turn off some borders when shaded decoration->borders( border_left, border_right, border_top, border_bottom ); @@ -774,13 +792,9 @@ 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())); @@ -915,8 +929,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 block_geometry_updates = 1, now it's ok to finally set the geometry - blockGeometryUpdates( false ); + if( was_unmanaged ) // manage() did postpone_geometry_updates = 1, now it's ok to finally set the geometry + postponeGeometryUpdates( false ); } /*! @@ -927,7 +941,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 ); @@ -942,7 +956,6 @@ 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, @@ -950,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(), frameId()); + XUnmapWindow( display(), frame ); XUnmapWindow( display(), wrapper ); XUnmapWindow( display(), client ); XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask ); @@ -1295,7 +1308,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 ) { @@ -1612,6 +1625,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 ); @@ -1643,12 +1708,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 @@ -1745,24 +1804,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() { diff --git a/group.cpp b/group.cpp index cf69600875..8d5ae57fb4 100644 --- a/group.cpp +++ b/group.cpp @@ -48,6 +48,8 @@ namespace KWinInternal #endif #ifdef ENABLE_TRANSIENCY_CHECK +static bool transiencyCheckNonExistent = false; + bool performTransiencyCheck() { bool ret = true; @@ -73,7 +75,8 @@ bool performTransiencyCheck() it2 != mains.end(); ++it2 ) { - if( !Workspace::self()->clients.contains( *it2 ) + if( transiencyCheckNonExistent + && !Workspace::self()->clients.contains( *it2 ) && !Workspace::self()->desktops.contains( *it2 )) { kDebug() << "TC:" << *it1 << " has non-existent main client " << endl; @@ -93,7 +96,8 @@ bool performTransiencyCheck() it2 != trans.end(); ++it2 ) { - if( !Workspace::self()->clients.contains( *it2 ) + if( transiencyCheckNonExistent + && !Workspace::self()->clients.contains( *it2 ) && !Workspace::self()->desktops.contains( *it2 )) { kDebug() << "TC:" << *it1 << " has non-existent transient " << endl; @@ -114,13 +118,16 @@ bool performTransiencyCheck() static QString transiencyCheckStartBt; static const Client* transiencyCheckClient; static int transiencyCheck = 0; -static void startTransiencyCheck( const QString& bt, const Client* c ) + +static void startTransiencyCheck( const QString& bt, const Client* c, bool ne ) { if( ++transiencyCheck == 1 ) { transiencyCheckStartBt = bt; transiencyCheckClient = c; } + if( ne ) + transiencyCheckNonExistent = true; } static void checkTransiency() { @@ -132,21 +139,32 @@ static void checkTransiency() kdDebug() << "CLIENT:" << transiencyCheckClient << endl; assert( false ); } + transiencyCheckNonExistent = false; } } class TransiencyChecker { public: - TransiencyChecker( const QString& bt, const Client*c ) { startTransiencyCheck( bt, c ); } + TransiencyChecker( const QString& bt, const Client*c ) { startTransiencyCheck( bt, c, false ); } ~TransiencyChecker() { checkTransiency(); } }; +void checkNonExistentClients() + { + startTransiencyCheck( kdBacktrace(), NULL, true ); + checkTransiency(); + } + #define TRANSIENCY_CHECK( c ) TransiencyChecker transiency_checker( kdBacktrace(), c ) #else #define TRANSIENCY_CHECK( c ) +void checkNonExistentClients() + { + } + #endif //******************************************** diff --git a/utils.h b/utils.h index 233b207075..3902e7629e 100644 --- a/utils.h +++ b/utils.h @@ -12,32 +12,6 @@ License. See the file "COPYING" for the exact licensing terms. #ifndef KWIN_UTILS_H #define KWIN_UTILS_H -#include -#include - -#include - -#ifdef HAVE_XRANDR -#include -#endif - -#ifdef HAVE_XCOMPOSITE -#include -#if XCOMPOSITE_MAJOR > 0 || XCOMPOSITE_MINOR >= 3 -#define HAVE_XCOMPOSITE_OVERLAY -#endif -#endif - -#ifdef HAVE_XDAMAGE -#include -#endif - -#ifdef HAVE_XFIXES -#include -#endif - -#include - #include #include #include @@ -48,13 +22,6 @@ License. See the file "COPYING" for the exact licensing terms. namespace KWinInternal { -#ifndef HAVE_XDAMAGE -typedef long Damage; -struct XDamageNotifyEvent - { - }; -#endif - const int SUPPORTED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask /*| NET::OverrideMask*/ | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask; @@ -72,18 +39,12 @@ const long ClientWinMask = KeyPressMask | KeyReleaseMask | const QPoint invalidPoint( INT_MIN, INT_MIN ); -class Toplevel; class Client; -class Unmanaged; class Group; class Options; -typedef QList< Toplevel* > ToplevelList; -typedef QList< const Toplevel* > ConstToplevelList; typedef QList< Client* > ClientList; typedef QList< const Client* > ConstClientList; -typedef QList< Unmanaged* > UnmanagedList; -typedef QList< const Unmanaged* > ConstUnmanagedList; typedef QList< Group* > GroupList; typedef QList< const Group* > ConstGroupList; @@ -126,7 +87,6 @@ enum allowed_t { Allowed }; // some enums to have more readable code, instead of using bools enum ForceGeometry_t { NormalGeometrySet, ForceGeometrySet }; - // Areas, mostly related to Xinerama enum clientAreaOption { @@ -149,32 +109,24 @@ enum ShadeMode ShadeActivated // "shaded", but visible due to alt+tab to the window }; -class Extensions +class Shape { public: + static bool available() { return kwin_shape_version > 0; } + static int version() { return kwin_shape_version; } // as 16*major+minor, i.e. two hex digits + static bool hasShape( WId w); + static int shapeEvent(); static void init(); - static bool shapeAvailable() { return has_shape; } - static int shapeNotifyEvent(); - static bool randrAvailable() { return has_randr; } - static int randrNotifyEvent(); - static bool damageAvailable() { return has_damage; } - static int damageNotifyEvent(); - static bool compositeAvailable() { return has_composite; } - static bool compositeOverlayAvailable() { return has_composite && has_composite_overlay; } - static bool fixesAvailable() { return has_fixes; } - static bool hasShape( Window w ); private: - static bool has_shape; - static int shape_event_base; - static bool has_randr; - static int randr_event_base; - static bool has_damage; - static int damage_event_base; - static bool has_composite; - static bool has_composite_overlay; - static bool has_fixes; + static int kwin_shape_version; + static int kwin_shape_event; }; +// compile with XShape older than 1.0 +#ifndef ShapeInput +const int ShapeInput = 2; +#endif + class Motif { public: @@ -274,38 +226,34 @@ int displayHeight() return XDisplayHeight( display(), DefaultScreen( display())); } -class Scene; -extern Scene* scene; -inline bool compositing() { return scene != NULL; } - // the docs say it's UrgencyHint, but it's often #defined as XUrgencyHint #ifndef UrgencyHint #define UrgencyHint XUrgencyHint #endif // for STL-like algo's -#define KWIN_CHECK_PREDICATE( name, cls, check ) \ +#define KWIN_CHECK_PREDICATE( name, check ) \ struct name \ { \ - inline bool operator()( const cls* cl ) { return check; }; \ + inline bool operator()( const Client* cl ) { return check; }; \ } -#define KWIN_COMPARE_PREDICATE( name, cls, type, check ) \ +#define KWIN_COMPARE_PREDICATE( name, type, check ) \ struct name \ { \ typedef type type_helper; /* in order to work also with type being 'const Client*' etc. */ \ inline name( const type_helper& compare_value ) : value( compare_value ) {}; \ - inline bool operator()( const cls* cl ) { return check; }; \ + inline bool operator()( const Client* cl ) { return check; }; \ const type_helper& value; \ } -#define KWIN_PROCEDURE( name, cls, action ) \ +#define KWIN_PROCEDURE( name, action ) \ struct name \ { \ - inline void operator()( cls* cl ) { action; }; \ + inline void operator()( Client* cl ) { action; }; \ } -KWIN_CHECK_PREDICATE( TruePredicate, Client, cl == cl /*true, avoid warning about 'cl' */ ); +KWIN_CHECK_PREDICATE( TruePredicate, cl == cl /*true, avoid warning about 'cl' */ ); template< typename T > Client* findClientInList( const ClientList& list, T predicate ) @@ -318,17 +266,6 @@ Client* findClientInList( const ClientList& list, T predicate ) return NULL; } -template< typename T > -Unmanaged* findUnmanagedInList( const UnmanagedList& list, T predicate ) - { - for ( UnmanagedList::ConstIterator it = list.begin(); it != list.end(); ++it) - { - if ( predicate( const_cast< const Unmanaged* >( *it))) - return *it; - } - return NULL; - } - inline int timestampCompare( Time time1, Time time2 ) // like strcmp() { @@ -343,6 +280,8 @@ Time timestampDiff( Time time1, Time time2 ) // returns time2 - time1 bool isLocalMachine( const QByteArray& host ); +void checkNonExistentClients(); + #ifndef KCMRULES // Qt dialogs emit no signal when closed :( class ShortcutDialog diff --git a/workspace.cpp b/workspace.cpp index a9638cef5a..1a4cba336d 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -516,6 +516,7 @@ void Workspace::addClient( Client* c, allowed_t ) updateStackingOrder( true ); // propagate new client if( c->isUtility() || c->isMenu() || c->isToolbar()) updateToolWindows( true ); + checkNonExistentClients(); } /*