diff --git a/client.cpp b/client.cpp index b9b4fe7a6..3641c367b 100644 --- a/client.cpp +++ b/client.cpp @@ -1,2277 +1,2307 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "client.h" #include #include #include #include #include #include #include #include #include #include #include #include "scripting/client.h" #include "scripting/scripting.h" #include "scripting/workspaceproxy.h" #include "bridge.h" #include "group.h" #include "workspace.h" #include "atoms.h" #include "notifications.h" #include "rules.h" #include "scene.h" #include "effects.h" #include "deleted.h" #include "paintredirector.h" #include "tabbox.h" #include #include #ifdef HAVE_XSYNC #include #endif #ifdef HAVE_XRENDER #include #endif // Put all externs before the namespace statement to allow the linker // to resolve them properly namespace KWin { // Creating a client: // - only by calling Workspace::createClient() // - it creates a new client and calls manage() for it // // Destroying a client: // - destroyClient() - only when the window itself has been destroyed // - releaseWindow() - the window is kept, only the client itself is destroyed /** * \class Client client.h * \brief The Client class encapsulates a window decoration frame. */ /** * This ctor is "dumb" - it only initializes data. All the real initialization * is done in manage(). */ Client::Client( Workspace* ws ) : Toplevel( ws ) , client( None ) , wrapper( None ) , decoration( NULL ) , bridge( new Bridge( this )) , move_faked_activity( false ) , move_resize_grab_window( None ) , move_resize_has_keyboard_grab( false ) , transient_for( NULL ) , transient_for_id( None ) , original_transient_for_id( None ) , autoRaiseTimer( NULL ) , shadeHoverTimer( NULL ) , delayedMoveResizeTimer( NULL ) , in_group( NULL ) , window_group( None ) , client_group( NULL ) , in_layer( UnknownLayer ) , ping_timer( NULL ) , process_killer( NULL ) , user_time( CurrentTime ) // Not known yet , allowed_actions( 0 ) , block_geometry_updates( 0 ) , pending_geometry_update( PendingGeometryNone ) , shade_geometry_change( false ) #ifdef HAVE_XSYNC , sync_counter( None ) , sync_alarm( None ) #endif , sync_timeout( NULL ) , sync_resize_pending( false ) , border_left( 0 ) , border_right( 0 ) , border_top( 0 ) , border_bottom( 0 ) , padding_left( 0 ) , padding_right( 0 ) , padding_top( 0 ) , padding_bottom( 0 ) , sm_stacking_order( -1 ) , demandAttentionKNotifyTimer( NULL ) , paintRedirector( 0 ) , electricMaximizing( false ) + , activitiesDefined( false ) + , needsSessionInteract(false) { // TODO: Do all as initialization scriptCache = new QHash(); // Set the initial mapping state mapping_state = Withdrawn; quick_tile_mode = QuickTileNone; geom_pretile = QRect( 0, 0, 0, 0 ); desk = 0; // No desktop yet mode = PositionCenter; buttonDown = false; moveResizeMode = false; info = NULL; shade_mode = ShadeNone; active = false; deleting = false; keep_above = false; keep_below = false; motif_may_move = true; motif_may_resize = true; motif_may_close = true; fullscreen_mode = FullScreenNone; skip_taskbar = false; original_skip_taskbar = false; minimized = false; hidden = false; modal = false; noborder = false; app_noborder = false; motif_noborder = false; urgency = false; ignore_focus_stealing = false; demands_attention = false; check_active_modal = false; Pdeletewindow = 0; Ptakefocus = 0; Ptakeactivity = 0; Pcontexthelp = 0; Pping = 0; input = false; skip_pager = false; max_mode = MaximizeRestore; maxmode_restore = MaximizeRestore; cmap = None; //Client to workspace connections require that each //client constructed be connected to the workspace wrapper // TabBoxClient m_tabBoxClient = new TabBox::TabBoxClientImpl(); m_tabBoxClient->setClient( this ); geom = QRect( 0, 0, 100, 100 ); // So that decorations don't start with size being (0,0) client_size = QSize( 100, 100 ); #if defined(HAVE_XSYNC) || defined(HAVE_XDAMAGE) ready_for_painting = false; // wait for first damage or sync reply #endif // SELI TODO: Initialize xsizehints?? } /** * "Dumb" destructor. */ Client::~Client() { //SWrapper::Client::clientRelease(this); #ifdef HAVE_XSYNC if( sync_alarm != None ) XSyncDestroyAlarm( display(), sync_alarm ); #endif assert(!moveResizeMode); assert( client == None ); assert( wrapper == None ); //assert( frameId() == None ); assert( decoration == NULL ); assert( block_geometry_updates == 0 ); assert( !check_active_modal ); delete bridge; delete m_tabBoxClient; delete scriptCache; } // Use destroyClient() or releaseWindow(), Client instances cannot be deleted directly void Client::deleteClient( Client* c, allowed_t ) { delete c; } /** * Releases the window. The client has done its job and the window is still existing. */ void Client::releaseWindow( bool on_shutdown ) { assert( !deleting ); deleting = true; Deleted* del = Deleted::create( this ); if( effects ) { static_cast(effects)->windowClosed( effectWindow()); scene->windowClosed( this, del ); } finishCompositing(); workspace()->discardUsedWindowRules( this, true ); // Remove ForceTemporarily rules StackingUpdatesBlocker blocker( workspace()); if (moveResizeMode) leaveMoveResize(); finishWindowRules(); ++block_geometry_updates; if( isOnCurrentDesktop() && isShown( true )) addWorkspaceRepaint( visibleRect() ); // 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(); exportMappingState( 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) if( !on_shutdown ) workspace()->clientHidden( this ); XUnmapWindow( display(), frameId()); // Destroying decoration would cause ugly visual effect destroyDecoration(); cleanGrouping(); if( clientGroup() ) clientGroup()->remove( this, QRect(), true ); if( !on_shutdown ) { workspace()->removeClient( this, Allowed ); // Only when the window is being unmapped, not when closing down KWin (NETWM sections 5.5,5.7) info->setDesktop( 0 ); desk = 0; info->setState( 0, info->state()); // Reset all state flags } XDeleteProperty( display(), client, atoms->kde_net_wm_user_creation_time); XDeleteProperty( display(), client, atoms->net_frame_extents ); XDeleteProperty( display(), client, atoms->kde_net_wm_frame_strut ); XReparentWindow( display(), client, rootWindow(), x(), y()); XRemoveFromSaveSet( display(), client ); XSelectInput( display(), client, NoEventMask ); if( on_shutdown ) // Map the window, so it can be found after another WM is started XMapWindow( display(), client ); // TODO: Preserve minimized, shaded etc. state? else // Make sure it's not mapped if the app unmapped it (#65279). The app // may do map+unmap before we initially map the window by calling rawShow() from manage(). XUnmapWindow( display(), client ); 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 disownDataPassedToDeleted(); del->unrefWindow(); checkNonExistentClients(); deleteClient( this, Allowed ); ungrabXServer(); } /** * Like releaseWindow(), but this one is called when the window has been already destroyed * (E.g. The application closed it) */ void Client::destroyClient() { assert( !deleting ); deleting = true; Deleted* del = Deleted::create( this ); if( effects ) { static_cast(effects)->windowClosed( effectWindow()); scene->windowClosed( this, del ); } finishCompositing(); workspace()->discardUsedWindowRules( this, true ); // Remove ForceTemporarily rules StackingUpdatesBlocker blocker( workspace()); if (moveResizeMode) leaveMoveResize(); finishWindowRules(); ++block_geometry_updates; if( isOnCurrentDesktop() && isShown( true )) addWorkspaceRepaint( visibleRect() ); setModal( false ); hidden = true; // So that it's not considered visible anymore workspace()->clientHidden( this ); destroyDecoration(); cleanGrouping(); if( clientGroup() ) clientGroup()->remove( this, QRect(), true ); workspace()->removeClient( this, Allowed ); 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 disownDataPassedToDeleted(); del->unrefWindow(); checkNonExistentClients(); deleteClient( this, Allowed ); } void Client::updateDecoration( bool check_workspace_pos, bool force ) { if( !force && (( decoration == NULL && noBorder() ) || ( decoration != NULL && !noBorder() ))) return; QRect oldgeom = geometry(); blockGeometryUpdates( true ); if( force ) destroyDecoration(); if( !noBorder() ) { setMask( QRegion()); // Reset shape mask decoration = workspace()->createDecoration( bridge ); // TODO: Check decoration's minimum size? decoration->init(); decoration->widget()->installEventFilter( this ); XReparentWindow( display(), decoration->widget()->winId(), frameId(), 0, 0 ); decoration->widget()->lower(); decoration->borders( border_left, border_right, border_top, border_bottom ); padding_left = padding_right = padding_top = padding_bottom = 0; if (KDecorationUnstable *deco2 = dynamic_cast(decoration)) deco2->padding( padding_left, padding_right, padding_top, padding_bottom ); XMoveWindow( display(), decoration->widget()->winId(), -padding_left, -padding_top ); move( calculateGravitation( false )); plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); paintRedirector = new PaintRedirector( decoration->widget()); connect( paintRedirector, SIGNAL( paintPending()), SLOT( repaintDecorationPending())); resizeDecorationPixmaps(); if( compositing() ) discardWindowPixmap(); if( scene != NULL ) scene->windowGeometryShapeChanged( this ); if( effects != NULL ) static_cast(effects)->windowGeometryShapeChanged( effectWindow(), oldgeom ); } else destroyDecoration(); if( check_workspace_pos ) checkWorkspacePosition(); blockGeometryUpdates( false ); if( !noBorder() ) decoration->widget()->show(); updateFrameExtents(); } void Client::destroyDecoration() { QRect oldgeom = geometry(); if( decoration != NULL ) { delete decoration; decoration = NULL; QPoint grav = calculateGravitation( true ); border_left = border_right = border_top = border_bottom = 0; setMask( QRegion()); // Reset shape mask plainResize( sizeForClientSize( clientSize()), ForceGeometrySet ); move( grav ); delete paintRedirector; paintRedirector = NULL; decorationPixmapLeft = decorationPixmapRight = decorationPixmapTop = decorationPixmapBottom = QPixmap(); if( compositing() ) discardWindowPixmap(); if( scene != NULL && !deleting ) scene->windowGeometryShapeChanged( this ); if( effects != NULL && !deleting ) static_cast(effects)->windowGeometryShapeChanged( effectWindow(), oldgeom ); } } bool Client::checkBorderSizes( bool also_resize ) { if( decoration == NULL ) return false; int new_left = 0, new_right = 0, new_top = 0, new_bottom = 0; if (KDecorationUnstable *deco2 = dynamic_cast(decoration)) deco2->padding( new_left, new_right, new_top, new_bottom ); if (padding_left != new_left || padding_top != new_top) XMoveWindow( display(), decoration->widget()->winId(), -new_left, -new_top ); padding_left = new_left; padding_right = new_right; padding_top = new_top; padding_bottom = new_bottom; decoration->borders( new_left, new_right, new_top, new_bottom ); if( new_left == border_left && new_right == border_right && new_top == border_top && new_bottom == border_bottom ) return false; if( !also_resize ) { border_left = new_left; border_right = new_right; border_top = new_top; border_bottom = new_bottom; return true; } GeometryUpdatesBlocker blocker( this ); move( calculateGravitation( true )); border_left = new_left; border_right = new_right; border_top = new_top; border_bottom = new_bottom; move( calculateGravitation( false )); plainResize( sizeForClientSize( clientSize() ), ForceGeometrySet ); checkWorkspacePosition(); return true; } void Client::triggerDecorationRepaint() { if( decoration != NULL ) decoration->widget()->update(); } void Client::layoutDecorationRects(QRect &left, QRect &top, QRect &right, QRect &bottom, Client::CoordinateMode mode) const { QRect r = decoration->widget()->rect(); if (mode == WindowRelative) r.translate(-padding_left, -padding_top); NETStrut strut = info->frameOverlap(); // Ignore the overlap strut when compositing is disabled if (!compositing() || !Workspace::self()->decorationSupportsFrameOverlap()) strut.left = strut.top = strut.right = strut.bottom = 0; else if (strut.left == -1 && strut.top == -1 && strut.right == -1 && strut.bottom == -1) { top = QRect(r.x(), r.y(), r.width(), r.height() / 3); left = QRect(r.x(), r.y() + top.height(), width() / 2, r.height() / 3); right = QRect(r.x() + left.width(), r.y() + top.height(), r.width() - left.width(), left.height()); bottom = QRect(r.x(), r.y() + top.height() + left.height(), r.width(), r.height() - left.height() - top.height()); return; } top = QRect(r.x(), r.y(), r.width(), padding_top + border_top + strut.top); bottom = QRect(r.x(), r.y() + r.height() - padding_bottom - border_bottom - strut.bottom, r.width(), padding_bottom + border_bottom + strut.bottom); left = QRect(r.x(), r.y() + top.height(), padding_left + border_left + strut.left, r.height() - top.height() - bottom.height()); right = QRect(r.x() + r.width() - padding_right - border_right - strut.right, r.y() + top.height(), padding_right + border_right + strut.right, r.height() - top.height() - bottom.height()); } QRegion Client::decorationPendingRegion() const { if (!paintRedirector) return QRegion(); return paintRedirector->scheduledRepaintRegion().translated( x() - padding_left, y() - padding_top ); } void Client::repaintDecorationPending() { if (compositing()) { // The scene will update the decoration pixmaps in the next painting pass // if it has not been already repainted before const QRegion r = paintRedirector->scheduledRepaintRegion(); if (!r.isEmpty()) Workspace::self()->addRepaint( r.translated( x() - padding_left, y() - padding_top ) ); } else ensureDecorationPixmapsPainted(); } bool Client::decorationPixmapRequiresRepaint() { if (!paintRedirector) return false; QRegion r = paintRedirector->pendingRegion(); return !r.isEmpty(); } void Client::ensureDecorationPixmapsPainted() { if (!paintRedirector) return; QRegion r = paintRedirector->pendingRegion(); if (r.isEmpty()) return; QPixmap p = paintRedirector->performPendingPaint(); QRect lr, rr, tr, br; layoutDecorationRects( lr, tr, rr, br, DecorationRelative ); repaintDecorationPixmap( decorationPixmapLeft, lr, p, r ); repaintDecorationPixmap( decorationPixmapRight, rr, p, r ); repaintDecorationPixmap( decorationPixmapTop, tr, p, r ); repaintDecorationPixmap( decorationPixmapBottom, br, p, r ); if (!compositing()) { // Blit the pixmaps to the frame window layoutDecorationRects( lr, tr, rr, br, WindowRelative ); #ifdef HAVE_XRENDER if (Extensions::renderAvailable()) { XRenderPictFormat* format = XRenderFindVisualFormat( display(), visual()); XRenderPictureAttributes pa; pa.subwindow_mode = IncludeInferiors; Picture pic = XRenderCreatePicture( display(), frameId(), format, CPSubwindowMode, &pa ); XRenderComposite( display(), PictOpSrc, decorationPixmapLeft.x11PictureHandle(), None, pic, 0, 0, 0, 0, lr.x(), lr.y(), lr.width(), lr.height() ); XRenderComposite( display(), PictOpSrc, decorationPixmapRight.x11PictureHandle(), None, pic, 0, 0, 0, 0, rr.x(), rr.y(), rr.width(), rr.height() ); XRenderComposite( display(), PictOpSrc, decorationPixmapTop.x11PictureHandle(), None, pic, 0, 0, 0, 0, tr.x(), tr.y(), tr.width(), tr.height() ); XRenderComposite( display(), PictOpSrc, decorationPixmapBottom.x11PictureHandle(), None, pic, 0, 0, 0, 0, br.x(), br.y(), br.width(), br.height() ); XRenderFreePicture( display(), pic ); // TODO don't recreate pictures all the time? } else #endif { XGCValues values; values.subwindow_mode = IncludeInferiors; GC gc = XCreateGC( display(), rootWindow(), GCSubwindowMode, &values ); XCopyArea( display(), decorationPixmapLeft.handle(), frameId(), gc, 0, 0, lr.width(), lr.height(), lr.x(), lr.y() ); XCopyArea( display(), decorationPixmapRight.handle(), frameId(), gc, 0, 0, rr.width(), rr.height(), rr.x(), rr.y() ); XCopyArea( display(), decorationPixmapTop.handle(), frameId(), gc, 0, 0, tr.width(), tr.height(), tr.x(), tr.y() ); XCopyArea( display(), decorationPixmapBottom.handle(), frameId(), gc, 0, 0, br.width(), br.height(), br.x(), br.y() ); XFreeGC( display(), gc ); } } else XSync( display(), false ); } void Client::repaintDecorationPixmap( QPixmap& pix, const QRect& r, const QPixmap& src, QRegion reg ) { if( !r.isValid()) return; QRect b = reg.boundingRect(); reg &= r; if( reg.isEmpty()) return; QPainter pt( &pix ); pt.translate( -r.topLeft() ); pt.setCompositionMode( QPainter::CompositionMode_Source ); pt.setClipRegion( reg ); pt.drawPixmap( b.topLeft(), src ); pt.end(); } void Client::resizeDecorationPixmaps() { QRect lr, rr, tr, br; layoutDecorationRects( lr, tr, rr, br, DecorationRelative ); if ( decorationPixmapTop.size() != tr.size() ) decorationPixmapTop = QPixmap( tr.size() ); if ( decorationPixmapBottom.size() != br.size() ) decorationPixmapBottom = QPixmap( br.size() ); if ( decorationPixmapLeft.size() != lr.size() ) decorationPixmapLeft = QPixmap( lr.size() ); if ( decorationPixmapRight.size() != rr.size() ) decorationPixmapRight = QPixmap( rr.size() ); #ifdef HAVE_XRENDER if ( Extensions::renderAvailable() ) { // Make sure the pixmaps are created with alpha channels decorationPixmapLeft.fill( Qt::transparent ); decorationPixmapRight.fill( Qt::transparent ); decorationPixmapTop.fill( Qt::transparent ); decorationPixmapBottom.fill( Qt::transparent ); } #endif triggerDecorationRepaint(); } QRect Client::transparentRect() const { if (isShade()) return QRect(); NETStrut strut = info->frameOverlap(); // Ignore the strut when compositing is disabled or the decoration doesn't support it if (!compositing() || !Workspace::self()->decorationSupportsFrameOverlap()) strut.left = strut.top = strut.right = strut.bottom = 0; else if (strut.left == -1 && strut.top == -1 && strut.right == -1 && strut.bottom == -1) return QRect(); const QRect r = QRect(clientPos(), clientSize()) .adjusted(strut.left, strut.top, -strut.right, -strut.bottom); if (r.isValid()) return r; return QRect(); } void Client::detectNoBorder() { if( shape()) { noborder = true; app_noborder = true; return; } switch( windowType()) { case NET::Desktop : case NET::Dock : case NET::TopMenu : case NET::Splash : noborder = true; app_noborder = true; break; case NET::Unknown : case NET::Normal : case NET::Toolbar : case NET::Menu : case NET::Dialog : case NET::Utility : noborder = false; break; default: abort(); } // NET::Override is some strange beast without clear definition, usually // just meaning "noborder", so let's treat it only as such flag, and ignore it as // a window type otherwise (SUPPORTED_WINDOW_TYPES_MASK doesn't include it) if( info->windowType( SUPPORTED_MANAGED_WINDOW_TYPES_MASK | NET::OverrideMask ) == NET::Override ) { noborder = true; app_noborder = true; } } void Client::updateFrameExtents() { NETStrut strut; strut.left = border_left; strut.right = border_right; strut.top = border_top; strut.bottom = border_bottom; info->setFrameExtents( strut ); } /** * Resizes the decoration, and makes sure the decoration widget gets resize event * even if the size hasn't changed. This is needed to make sure the decoration * re-layouts (e.g. when options()->moveResizeMaximizedWindows() changes, * the decoration may turn on/off some borders, but the actual size * of the decoration stays the same). */ void Client::resizeDecoration( const QSize& s ) { if( decoration == NULL ) return; QSize newSize = s + QSize(padding_left + padding_right, padding_top + padding_bottom); QSize oldSize = decoration->widget()->size(); decoration->resize( newSize ); if( oldSize == newSize ) { QResizeEvent e( newSize, oldSize ); QApplication::sendEvent( decoration->widget(), &e ); } else // oldSize != newSize { resizeDecorationPixmaps(); } } bool Client::noBorder() const { return noborder || isFullScreen(); } bool Client::userCanSetNoBorder() const { return !isFullScreen() && !isShade() && ( clientGroup() == NULL || !(clientGroup()->items().count() > 1)); } void Client::setNoBorder( bool set ) { if( !userCanSetNoBorder() ) return; set = rules()->checkNoBorder( set ); if( noborder == set ) return; noborder = set; updateDecoration( true, false ); updateWindowRules(); } void Client::checkNoBorder() { setNoBorder( app_noborder ); } void Client::updateShape() { if( shape() ) { // Workaround for #19644 - Shaped windows shouldn't have decoration if( !app_noborder ) { // Only when shape is detected for the first time, still let the user to override app_noborder = true; noborder = true; updateDecoration( true ); } } if( shape() && noBorder() ) XShapeCombineShape( display(), frameId(), ShapeBounding, clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeSet ); // Decoration mask (i.e. 'else' here) setting is done in setMask() // when the decoration calls it or when the decoration is created/destroyed updateInputShape(); if( compositing()) { addRepaintFull(); addWorkspaceRepaint( visibleRect() ); // In case shape change removes part of this window } if( scene != NULL ) scene->windowGeometryShapeChanged( this ); if( effects != NULL ) static_cast(effects)->windowGeometryShapeChanged( effectWindow(), geometry()); } static Window shape_helper_window = None; void Client::updateInputShape() { if( hiddenPreview() ) // Sets it to none, don't change return; if( Extensions::shapeInputAvailable()) { // 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) // TODO: It seems there is, after all - XShapeGetRectangles() - but maybe this is better if( shape_helper_window == None ) shape_helper_window = XCreateSimpleWindow( display(), rootWindow(), 0, 0, 1, 1, 0, 0, 0 ); XResizeWindow( display(), shape_helper_window, width(), height()); XShapeCombineShape( display(), shape_helper_window, ShapeInput, 0, 0, frameId(), ShapeBounding, ShapeSet ); XShapeCombineShape( display(), shape_helper_window, ShapeInput, clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeSubtract ); XShapeCombineShape( display(), shape_helper_window, ShapeInput, clientPos().x(), clientPos().y(), window(), ShapeInput, ShapeUnion ); XShapeCombineShape( display(), frameId(), ShapeInput, 0, 0, shape_helper_window, ShapeInput, ShapeSet ); } } void Client::setMask( const QRegion& reg, int mode ) { QRegion r = reg.translated( -padding_left, -padding_right ) & QRect( 0, 0, width(), height() ); if( _mask == r ) return; _mask = r; Window shape_window = frameId(); if( shape() ) { // The same way of applying a shape without strange intermediate states like above if( shape_helper_window == None ) shape_helper_window = XCreateSimpleWindow( display(), rootWindow(), 0, 0, 1, 1, 0, 0, 0 ); shape_window = shape_helper_window; } if( _mask.isEmpty() ) XShapeCombineMask( display(), shape_window, ShapeBounding, 0, 0, None, ShapeSet ); else if( mode == X::Unsorted ) XShapeCombineRegion( display(), shape_window, ShapeBounding, 0, 0, _mask.handle(), ShapeSet ); else { QVector< QRect > rects = _mask.rects(); XRectangle* xrects = new XRectangle[rects.count()]; for( int i = 0; i < rects.count(); ++i ) { xrects[i].x = rects[i].x(); xrects[i].y = rects[i].y(); xrects[i].width = rects[i].width(); xrects[i].height = rects[i].height(); } XShapeCombineRectangles( display(), shape_window, ShapeBounding, 0, 0, xrects, rects.count(), ShapeSet, mode ); delete[] xrects; } if( shape() ) { // The rest of the applyign using a temporary window XRectangle rec = { 0, 0, clientSize().width(), clientSize().height() }; XShapeCombineRectangles( display(), shape_helper_window, ShapeBounding, clientPos().x(), clientPos().y(), &rec, 1, ShapeSubtract, Unsorted ); XShapeCombineShape( display(), shape_helper_window, ShapeBounding, clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeUnion ); XShapeCombineShape( display(), frameId(), ShapeBounding, 0, 0, shape_helper_window, ShapeBounding, ShapeSet ); } if( scene != NULL ) scene->windowGeometryShapeChanged( this ); if( effects != NULL ) static_cast( effects )->windowGeometryShapeChanged( effectWindow(), geometry() ); updateShape(); } QRegion Client::mask() const { if( _mask.isEmpty() ) return QRegion( 0, 0, width(), height() ); return _mask; } void Client::hideClient( bool hide ) { if( hidden == hide ) return; hidden = hide; updateVisibility(); } /** * Returns whether the window is minimizable or not */ bool Client::isMinimizable() const { if( isSpecialWindow() ) return false; if( isTransient() ) { // #66868 - Let other xmms windows be minimized when the mainwindow is minimized bool shown_mainwindow = false; ClientList mainclients = mainClients(); for( ClientList::ConstIterator it = mainclients.constBegin(); it != mainclients.constEnd(); ++it ) if( (*it)->isShown( true )) shown_mainwindow = true; if( !shown_mainwindow ) return true; } #if 0 // This is here because kicker's taskbar doesn't provide separate entries // for windows with an explicitly given parent // TODO: perhaps this should be redone // Disabled for now, since at least modal dialogs should be minimizable // (resulting in the mainwindow being minimized too). if( transientFor() != NULL ) return false; #endif if( !wantsTabFocus() ) // SELI, TODO: - NET::Utility? why wantsTabFocus() - skiptaskbar? ? return false; return true; } /** * Minimizes this client plus its transients */ void Client::minimize( bool avoid_animation ) { if( !isMinimizable() || isMinimized() ) return; //Scripting call. Does not use a signal/slot mechanism //as ensuring connections was a bit difficult between //so many clients and the workspace SWrapper::WorkspaceProxy* ws_wrap = SWrapper::WorkspaceProxy::instance(); if(ws_wrap != 0) { ws_wrap->sl_clientMinimized(this); } emit s_minimized(); Notify::raise( Notify::Minimize ); minimized = true; updateVisibility(); updateAllowedActions(); workspace()->updateMinimizedOfTransients( this ); updateWindowRules(); workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast ); if( effects && !avoid_animation ) // TODO: Shouldn't it tell effects at least about the change? static_cast(effects)->windowMinimized( effectWindow()); // when tiling, request a rearrangement workspace()->notifyTilingWindowMinimizeToggled( this ); // Update states of all other windows in this group if( clientGroup() ) clientGroup()->updateStates( this ); } void Client::unminimize( bool avoid_animation ) { if( !isMinimized()) return; SWrapper::WorkspaceProxy* ws_wrap = SWrapper::WorkspaceProxy::instance(); if(ws_wrap != 0) { ws_wrap->sl_clientUnminimized(this); } emit s_unminimized(); Notify::raise( Notify::UnMinimize ); minimized = false; updateVisibility(); updateAllowedActions(); workspace()->updateMinimizedOfTransients( this ); updateWindowRules(); workspace()->updateAllTiles(); if( effects && !avoid_animation ) static_cast( effects )->windowUnminimized( effectWindow() ); // when tiling, request a rearrangement workspace()->notifyTilingWindowMinimizeToggled( this ); // Update states of all other windows in this group if( clientGroup() ) clientGroup()->updateStates( this ); } QRect Client::iconGeometry() const { NETRect r = info->iconGeometry(); QRect geom( r.pos.x, r.pos.y, r.size.width, r.size.height ); if( geom.isValid() ) return geom; else { // Check all mainwindows of this window (recursively) foreach( Client* mainwin, mainClients() ) { geom = mainwin->iconGeometry(); if( geom.isValid() ) return geom; } // No mainwindow (or their parents) with icon geometry was found return QRect(); } } bool Client::isShadeable() const { return !isSpecialWindow() && !noBorder(); } void Client::setShade( ShadeMode mode ) { if( !isShadeable()) return; mode = rules()->checkShade( mode ); if( shade_mode == mode ) return; bool was_shade = isShade(); ShadeMode was_shade_mode = shade_mode; shade_mode = mode; if( was_shade == isShade()) { if( decoration != NULL ) // Decoration may want to update after e.g. hover-shade changes decoration->shadeChange(); return; // No real change in shaded state } if( shade_mode == ShadeNormal ) { if( isShown( true ) && isOnCurrentDesktop() ) Notify::raise( Notify::ShadeUp ); } else if( shade_mode == ShadeNone ) { if( isShown( true ) && isOnCurrentDesktop() ) Notify::raise( Notify::ShadeDown ); } assert( decoration != NULL ); // noborder windows can't be shaded GeometryUpdatesBlocker blocker( this ); // Decorations may turn off some borders when shaded if( decoration ) decoration->borders( border_left, border_right, border_top, border_bottom ); // TODO: All this unmapping, resizing etc. feels too much duplicated from elsewhere if ( isShade()) { // shade_mode == ShadeNormal addWorkspaceRepaint( visibleRect() ); // Shade shade_geometry_change = true; QSize s( sizeForClientSize( QSize( clientSize() ))); s.setHeight( border_top + border_bottom ); XSelectInput( display(), wrapper, ClientWinMask ); // Avoid getting UnmapNotify XUnmapWindow( display(), wrapper ); XUnmapWindow( display(), client ); XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask ); plainResize( s ); shade_geometry_change = false; if( isActive()) { if( was_shade_mode == ShadeHover ) workspace()->activateNextClient( this ); else workspace()->focusToNull(); } } else { shade_geometry_change = true; QSize s( sizeForClientSize( clientSize() )); shade_geometry_change = false; plainResize( s ); if( shade_mode == ShadeHover || shade_mode == ShadeActivated ) setActive( true ); XMapWindow( display(), wrapperId() ); XMapWindow( display(), window() ); if ( isActive() ) workspace()->requestFocus( this ); } checkMaximizeGeometry(); info->setState( isShade() ? NET::Shaded : 0, NET::Shaded ); info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden ); discardWindowPixmap(); updateVisibility(); updateAllowedActions(); if( decoration ) decoration->shadeChange(); updateWindowRules(); // Update states of all other windows in this group if( clientGroup() ) clientGroup()->updateStates( this ); } void Client::shadeHover() { setShade( ShadeHover ); cancelShadeHoverTimer(); } void Client::shadeUnhover() { setShade( ShadeNormal ); cancelShadeHoverTimer(); } void Client::cancelShadeHoverTimer() { delete shadeHoverTimer; shadeHoverTimer = 0; } void Client::toggleShade() { // If the mode is ShadeHover or ShadeActive, cancel shade too setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone ); } void Client::updateVisibility() { if( deleting ) return; if( hidden && ( clientGroup() == NULL || clientGroup()->visible() == this )) { info->setState( NET::Hidden, NET::Hidden ); setSkipTaskbar( true, false ); // Also hide from taskbar if( compositing() && options->hiddenPreviews == HiddenPreviewsAlways ) internalKeep( Allowed ); else internalHide( Allowed ); return; } if( clientGroup() == NULL || clientGroup()->visible() == this ) setSkipTaskbar( original_skip_taskbar, false ); // Reset from 'hidden' if( minimized ) { info->setState( NET::Hidden, NET::Hidden ); if( compositing() && options->hiddenPreviews == HiddenPreviewsAlways ) internalKeep( Allowed ); else internalHide( Allowed ); return; } info->setState( 0, NET::Hidden ); if( !isOnCurrentDesktop()) { if( compositing() && options->hiddenPreviews != HiddenPreviewsNever ) internalKeep( Allowed ); else internalHide( Allowed ); return; } if( !isOnCurrentActivity()) { if( compositing() && options->hiddenPreviews != HiddenPreviewsNever ) internalKeep( Allowed ); else internalHide( Allowed ); return; } bool belongs_to_desktop = false; for( ClientList::ConstIterator it = group()->members().constBegin(); it != group()->members().constEnd(); ++it ) if( (*it)->isDesktop() ) { belongs_to_desktop = true; break; } if( !belongs_to_desktop && workspace()->showingDesktop()) workspace()->resetShowingDesktop( true ); internalShow( Allowed ); } /** * Sets the client window's mapping state. Possible values are * WithdrawnState, IconicState, NormalState. */ void Client::exportMappingState( int s ) { assert( client != None ); assert( !deleting || s == WithdrawnState ); if( s == WithdrawnState ) { XDeleteProperty( display(), window(), atoms->wm_state ); return; } assert( s == NormalState || s == IconicState ); unsigned long data[2]; data[0] = (unsigned long) s; data[1] = (unsigned long) None; XChangeProperty(display(), window(), atoms->wm_state, atoms->wm_state, 32, PropModeReplace, (unsigned char*)( data ), 2); } void Client::internalShow( allowed_t ) { if( mapping_state == Mapped ) return; MappingState old = mapping_state; mapping_state = Mapped; if( old == Unmapped || old == Withdrawn ) map( Allowed ); if( old == Kept ) updateHiddenPreview(); workspace()->checkUnredirect(); } void Client::internalHide( allowed_t ) { if( mapping_state == Unmapped ) return; MappingState old = mapping_state; mapping_state = Unmapped; if( old == Mapped || old == Kept ) unmap( Allowed ); if( old == Kept ) updateHiddenPreview(); addWorkspaceRepaint( visibleRect() ); workspace()->clientHidden( this ); workspace()->checkUnredirect(); } void Client::internalKeep( allowed_t ) { assert( compositing() ); if( mapping_state == Kept ) return; MappingState old = mapping_state; mapping_state = Kept; if( old == Unmapped || old == Withdrawn ) map( Allowed ); updateHiddenPreview(); addWorkspaceRepaint( visibleRect() ); workspace()->clientHidden( this ); workspace()->checkUnredirect(); } /** * Maps (shows) the client. Note that it is mapping state of the frame, * not necessarily the client window itself (i.e. a shaded window is here * considered mapped, even though it is in IconicState). */ void Client::map( allowed_t ) { // 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(); if( decoration != NULL ) decoration->widget()->show(); // Not really necessary, but let it know the state XMapWindow( display(), frameId()); if( !isShade()) { XMapWindow( display(), wrapper ); XMapWindow( display(), client ); exportMappingState( NormalState ); } else exportMappingState( IconicState ); } /** * Unmaps the client. Again, this is about the frame. */ void Client::unmap( allowed_t ) { // 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, // which won't be missed, so this shouldn't be a problem. The chance the real UnmapNotify // 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(), wrapper ); XUnmapWindow( display(), client ); XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask ); if( decoration != NULL ) decoration->widget()->hide(); // Not really necessary, but let it know the state exportMappingState( IconicState ); } /** * XComposite doesn't keep window pixmaps of unmapped windows, which means * there wouldn't be any previews of windows that are minimized or on another * virtual desktop. Therefore rawHide() actually keeps such windows mapped. * However special care needs to be taken so that such windows don't interfere. * Therefore they're put very low in the stacking order and they have input shape * set to none, which hopefully is enough. If there's no input shape available, * then it's hoped that there will be some other desktop above it *shrug*. * Using normal shape would be better, but that'd affect other things, e.g. painting * of the actual preview. */ void Client::updateHiddenPreview() { if( hiddenPreview() ) { workspace()->forceRestacking(); if( Extensions::shapeInputAvailable() ) XShapeCombineRectangles( display(), frameId(), ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted ); } else { workspace()->forceRestacking(); updateInputShape(); } } void Client::sendClientMessage( Window w, Atom a, Atom protocol, long data1, long data2, long data3 ) { XEvent ev; long mask; memset( &ev, 0, sizeof( ev )); ev.xclient.type = ClientMessage; ev.xclient.window = w; ev.xclient.message_type = a; ev.xclient.format = 32; ev.xclient.data.l[0] = protocol; ev.xclient.data.l[1] = xTime(); ev.xclient.data.l[2] = data1; ev.xclient.data.l[3] = data2; ev.xclient.data.l[4] = data3; mask = 0L; if( w == rootWindow() ) mask = SubstructureRedirectMask; // Magic! XSendEvent( display(), w, False, mask, &ev ); } /** * Returns whether the window may be closed (have a close button) */ bool Client::isCloseable() const { return rules()->checkCloseable( motif_may_close && !isSpecialWindow() ); } /** * Closes the window by either sending a delete_window message or using XKill. */ void Client::closeWindow() { if( !isCloseable() ) return; // Update user time, because the window may create a confirming dialog. updateUserTime(); if ( Pdeletewindow ) { Notify::raise( Notify::Close ); sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window); pingWindow(); } else // Client will not react on wm_delete_window. We have not choice // but destroy his connection to the XServer. killWindow(); } /** * Kills the window via XKill */ void Client::killWindow() { kDebug( 1212 ) << "Client::killWindow():" << caption(); // Not sure if we need an Notify::Kill or not.. until then, use // Notify::Close Notify::raise( Notify::Close ); if( isDialog() ) Notify::raise( Notify::TransDelete ); if( isNormalWindow() ) Notify::raise( Notify::Delete ); killProcess( false ); XKillClient(display(), window() ); // Always kill this client at the server destroyClient(); } /** * Send a ping to the window using _NET_WM_PING if possible if it * doesn't respond within a reasonable time, it will be killed. */ void Client::pingWindow() { if( !Pping ) return; // Can't ping :( if( options->killPingTimeout == 0 ) return; // Turned off if( ping_timer != NULL ) return; // Pinging already ping_timer = new QTimer( this ); connect( ping_timer, SIGNAL( timeout() ), SLOT( pingTimeout() )); ping_timer->setSingleShot( true ); ping_timer->start( options->killPingTimeout ); ping_timestamp = xTime(); workspace()->sendPingToWindow( window(), ping_timestamp ); } void Client::gotPing( Time timestamp ) { // Just plain compare is not good enough because of 64bit and truncating and whatnot if( NET::timestampCompare( timestamp, ping_timestamp ) != 0 ) return; delete ping_timer; ping_timer = NULL; if( process_killer != NULL ) { process_killer->kill(); // Recycle when the process manager has noticed that the process exited // a delete process_killer here sometimes causes a hang in waitForFinished connect(process_killer, SIGNAL( finished(int, QProcess::ExitStatus) ), process_killer, SLOT( deleteLater() )); process_killer = NULL; } } void Client::pingTimeout() { kDebug( 1212 ) << "Ping timeout:" << caption(); ping_timer->deleteLater(); ping_timer = NULL; killProcess( true, ping_timestamp ); } void Client::killProcess( bool ask, Time timestamp ) { if( process_killer != NULL ) return; Q_ASSERT( !ask || timestamp != CurrentTime ); QByteArray machine = wmClientMachine( true ); pid_t pid = info->pid(); if( pid <= 0 || machine.isEmpty()) // Needed properties missing return; kDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")"; if( !ask ) { if( machine != "localhost" ) { QStringList lst; lst << machine << "kill" << QString::number( pid ); QProcess::startDetached( "xon",lst ); } else ::kill( pid, SIGTERM ); } else { process_killer = new QProcess( this ); connect( process_killer, SIGNAL( error(QProcess::ProcessError) ), SLOT( processKillerExited() )); connect( process_killer, SIGNAL( finished(int, QProcess::ExitStatus) ), SLOT( processKillerExited() )); process_killer->start( KStandardDirs::findExe( "kwin_killer_helper" ), QStringList() << "--pid" << QByteArray().setNum( unsigned( pid )) << "--hostname" << machine << "--windowname" << caption() << "--applicationname" << resourceClass() << "--wid" << QString::number( window() ) << "--timestamp" << QString::number( timestamp )); } } void Client::processKillerExited() { kDebug( 1212 ) << "Killer exited"; delete process_killer; process_killer = NULL; } void Client::setSkipTaskbar( bool b, bool from_outside ) { int was_wants_tab_focus = wantsTabFocus(); if( from_outside ) { b = rules()->checkSkipTaskbar( b ); original_skip_taskbar = b; } if( b == skipTaskbar() ) return; skip_taskbar = b; info->setState( b ? NET::SkipTaskbar : 0, NET::SkipTaskbar ); updateWindowRules(); if( was_wants_tab_focus != wantsTabFocus()) workspace()->updateFocusChains( this, isActive() ? Workspace::FocusChainMakeFirst : Workspace::FocusChainUpdate ); } void Client::setSkipPager( bool b ) { b = rules()->checkSkipPager( b ); if( b == skipPager() ) return; skip_pager = b; info->setState( b ? NET::SkipPager : 0, NET::SkipPager ); updateWindowRules(); } void Client::setSkipSwitcher( bool set ) { set = rules()->checkSkipSwitcher( set ); if( set == skipSwitcher() ) return; skip_switcher = set; updateWindowRules(); } void Client::setModal( bool m ) { // Qt-3.2 can have even modal normal windows :( if( modal == m ) return; modal = m; if( !modal ) return; // Changing modality for a mapped window is weird (?) // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG } void Client::setDesktop( int desktop ) { if( desktop != NET::OnAllDesktops ) // Do range check desktop = qMax( 1, qMin( workspace()->numberOfDesktops(), desktop )); desktop = qMin( workspace()->numberOfDesktops(), rules()->checkDesktop( desktop )); if( desk == desktop ) return; int was_desk = desk; desk = desktop; info->setDesktop( desktop ); if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops )) { // onAllDesktops changed if( isShown( true )) Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops ); workspace()->updateOnAllDesktopsOfTransients( this ); } if( decoration != NULL ) decoration->desktopChange(); ClientList transients_stacking_order = workspace()->ensureStackingOrder( transients() ); for( ClientList::ConstIterator it = transients_stacking_order.constBegin(); it != transients_stacking_order.constEnd(); ++it ) (*it)->setDesktop( desktop ); if( isModal()) // if a modal dialog is moved, move the mainwindow with it as otherwise // the (just moved) modal dialog will confusingly return to the mainwindow with // the next desktop change { foreach( Client* c2, mainClients()) c2->setDesktop( desktop ); } workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst ); updateVisibility(); updateWindowRules(); // Update states of all other windows in this group if( clientGroup() ) clientGroup()->updateStates( this ); } /** * Sets whether the client is on @p activity. * If you remove it from its last activity, then it's on all activities. * * Note: If it was on all activities and you try to remove it from one, nothing will happen; * I don't think that's an important enough use case to handle here. */ void Client::setOnActivity( const QString &activity, bool enable ) { QStringList newActivitiesList = activities(); if( newActivitiesList.contains(activity) == enable ) //nothing to do return; if (enable) { QStringList allActivities = workspace()->activityList(); if( !allActivities.contains(activity) ) //bogus ID return; newActivitiesList.append(activity); } else newActivitiesList.removeOne(activity); setOnActivities( newActivitiesList ); } /** * set exactly which activities this client is on */ void Client::setOnActivities( QStringList newActivitiesList ) { QStringList allActivities = workspace()->activityList(); if( newActivitiesList.size() == allActivities.size() || newActivitiesList.isEmpty() ) { setOnAllActivities(true); return; } QByteArray joined = newActivitiesList.join(",").toAscii(); char *data = joined.data(); activityList = newActivitiesList; XChangeProperty(display(), window(), atoms->activities, XA_STRING, 8, PropModeReplace, (unsigned char *)data, joined.size()); updateActivities( false ); } /** * update after activities changed */ void Client::updateActivities( bool includeTransients ) { /* FIXME do I need this? if( decoration != NULL ) decoration->desktopChange(); */ if( includeTransients ) workspace()->updateOnAllActivitiesOfTransients( this ); workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst ); updateVisibility(); updateWindowRules(); // Update states of all other windows in this group if( clientGroup() ) clientGroup()->updateStates( this ); } /** * Returns the virtual desktop within the workspace() the client window * is located in, 0 if it isn't located on any special desktop (not mapped yet), * or NET::OnAllDesktops. Do not use desktop() directly, use * isOnDesktop() instead. */ int Client::desktop() const - { - return desk; +{ + if (needsSessionInteract) { + return NET::OnAllDesktops; } + return desk; +} /** * Returns the list of activities the client window is on. * if it's on all activities, the list will be empty. * Don't use this, use isOnActivity() and friends (from class Toplevel) */ QStringList Client::activities() const { + if (needsSessionInteract) { + return QStringList(); + } return activityList; } void Client::setOnAllDesktops( bool b ) { if(( b && isOnAllDesktops() ) || ( !b && !isOnAllDesktops() )) return; if( b ) setDesktop( NET::OnAllDesktops ); else setDesktop( workspace()->currentDesktop()); // Update states of all other windows in this group if( clientGroup() ) clientGroup()->updateStates( this ); } /** * if @p on is true, sets on all activities. * if it's false, sets it to only be on the current activity */ void Client::setOnAllActivities( bool on ) { if( on == isOnAllActivities() ) return; if( on ) { activityList.clear(); - XDeleteProperty( display(), window(), atoms->activities ); + XChangeProperty(display(), window(), atoms->activities, XA_STRING, 8, + PropModeReplace, (const unsigned char *)"ALL", 3); updateActivities( true ); } else { setOnActivity(Workspace::self()->currentActivity(), true); workspace()->updateOnAllActivitiesOfTransients( this ); } } /** * Performs activation and/or raising of the window */ void Client::takeActivity( int flags, bool handled, allowed_t ) { if( !handled || !Ptakeactivity ) { if( flags & ActivityFocus ) takeFocus( Allowed ); if( flags & ActivityRaise ) workspace()->raiseClient( this ); return; } #ifndef NDEBUG static Time previous_activity_timestamp; static Client* previous_client; //if( previous_activity_timestamp == xTime() && previous_client != this ) // { // kDebug( 1212 ) << "Repeated use of the same X timestamp for activity"; // kDebug( 1212 ) << kBacktrace(); // } previous_activity_timestamp = xTime(); previous_client = this; #endif workspace()->sendTakeActivity( this, xTime(), flags ); } /** * Performs the actual focusing of the window using XSetInputFocus and WM_TAKE_FOCUS */ void Client::takeFocus( allowed_t ) { #ifndef NDEBUG static Time previous_focus_timestamp; static Client* previous_client; //if( previous_focus_timestamp == xTime() && previous_client != this ) // { // kDebug( 1212 ) << "Repeated use of the same X timestamp for focus"; // kDebug( 1212 ) << kBacktrace(); // } previous_focus_timestamp = xTime(); previous_client = this; #endif if( rules()->checkAcceptFocus( input )) XSetInputFocus( display(), window(), RevertToPointerRoot, xTime() ); if( Ptakefocus ) sendClientMessage( window(), atoms->wm_protocols, atoms->wm_take_focus ); workspace()->setShouldGetFocus( this ); } /** * Returns whether the window provides context help or not. If it does, * you should show a help menu item or a help button like '?' and call * contextHelp() if this is invoked. * * \sa contextHelp() */ bool Client::providesContextHelp() const { return Pcontexthelp; } /** * Invokes context help on the window. Only works if the window * actually provides context help. * * \sa providesContextHelp() */ void Client::showContextHelp() { if( Pcontexthelp ) { sendClientMessage( window(), atoms->wm_protocols, atoms->net_wm_context_help ); QWhatsThis::enterWhatsThisMode(); // SELI TODO: ? } } /** * Fetches the window's caption (WM_NAME property). It will be * stored in the client's caption(). */ void Client::fetchName() { setCaption( readName()); } QString Client::readName() const { if( info->name() && info->name()[0] != '\0' ) return QString::fromUtf8( info->name() ); else return KWindowSystem::readNameProperty( window(), XA_WM_NAME ); } KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, Client, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption()); // The list is taken from http://www.unicode.org/reports/tr9/ (#154840) QChar LRM(0x200E); QChar RLM(0x200F); QChar LRE(0x202A); QChar RLE(0x202B); QChar LRO(0x202D); QChar RLO(0x202E); QChar PDF(0x202C); void Client::setCaption( const QString& _s, bool force ) { QString s = _s; if( s != cap_normal || force ) { bool reset_name = force; for( int i = 0; i < s.length(); ++i ) if( !s[i].isPrint() ) s[i] = QChar( ' ' ); cap_normal = s; bool was_suffix = ( !cap_suffix.isEmpty() ); QString machine_suffix; if( wmClientMachine( false ) != "localhost" && !isLocalMachine( wmClientMachine( false ))) machine_suffix = QString( " <@" ) + wmClientMachine( true ) + '>' + LRM; QString shortcut_suffix = !shortcut().isEmpty() ? ( " {" + shortcut().toString() + '}' ) : QString(); cap_suffix = machine_suffix + shortcut_suffix; if(( !isSpecialWindow() || isToolbar() ) && workspace()->findClient( FetchNameInternalPredicate( this ))) { int i = 2; do { cap_suffix = machine_suffix + " <" + QString::number(i) + '>' + LRM + shortcut_suffix; i++; } while ( workspace()->findClient( FetchNameInternalPredicate( this ))); info->setVisibleName( caption().toUtf8() ); reset_name = false; } if(( was_suffix && cap_suffix.isEmpty() ) || reset_name ) { // If it was new window, it may have old value still set, if the window is reused info->setVisibleName( "" ); info->setVisibleIconName( "" ); } else if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty()) // Keep the same suffix in iconic name if it's set info->setVisibleIconName( ( cap_iconic + cap_suffix ).toUtf8() ); if( isManaged() && decoration != NULL ) { if (client_group) client_group->updateItems(); decoration->captionChange(); } } } void Client::updateCaption() { setCaption( cap_normal, true ); } void Client::fetchIconicName() { QString s; if( info->iconName() && info->iconName()[0] != '\0' ) s = QString::fromUtf8( info->iconName() ); else s = KWindowSystem::readNameProperty( window(), XA_WM_ICON_NAME ); if( s != cap_iconic ) { bool was_set = !cap_iconic.isEmpty(); cap_iconic = s; if( !cap_suffix.isEmpty()) { if( !cap_iconic.isEmpty()) // Keep the same suffix in iconic name if it's set info->setVisibleIconName( ( s + cap_suffix ).toUtf8() ); else if( was_set ) info->setVisibleIconName( "" ); } } } /** * \reimp */ QString Client::caption( bool full ) const { return full ? cap_normal + cap_suffix : cap_normal; } void Client::dontMoveResize() { buttonDown = false; stopDelayedMoveResize(); if( moveResizeMode ) finishMoveResize( false ); } void Client::setClientShown( bool shown ) { if( deleting ) return; // Don't change shown status if this client is being deleted if( shown && hidden ) { map( Allowed ); hidden = false; //updateVisibility(); //updateAllowedActions(); if( options->inactiveTabsSkipTaskbar ) setSkipTaskbar( false, false ); takeFocus( Allowed ); autoRaise(); workspace()->updateFocusChains( this, Workspace::FocusChainMakeFirst ); } if( !shown && !hidden ) { unmap( Allowed ); hidden = true; //updateVisibility(); //updateAllowedActions(); if( options->inactiveTabsSkipTaskbar ) setSkipTaskbar( true, false ); // TODO: Causes reshuffle of the taskbar // Don't move tabs to the end of the list when another tab get's activated if( !clientGroup() || clientGroup()->visible() == this ) workspace()->updateFocusChains( this, Workspace::FocusChainMakeLast ); addWorkspaceRepaint( visibleRect() ); } } void Client::getWMHints() { XWMHints* hints = XGetWMHints( display(), window() ); input = true; window_group = None; urgency = false; if( hints ) { if( hints->flags & InputHint ) input = hints->input; if( hints->flags & WindowGroupHint ) window_group = hints->window_group; urgency = !!( hints->flags & UrgencyHint ); // Need boolean, it's a uint bitfield XFree( (char*)hints ); } checkGroup(); updateUrgency(); updateAllowedActions(); // Group affects isMinimizable() } void Client::sl_activated() { emit s_activated(); } void Client::getMotifHints() { bool mgot_noborder, mnoborder, mresize, mmove, mminimize, mmaximize, mclose; Motif::readFlags( client, mgot_noborder, mnoborder, mresize, mmove, mminimize, mmaximize, mclose ); if( mgot_noborder ) { motif_noborder = mnoborder; // If we just got a hint telling us to hide decorations, we do so. if ( motif_noborder ) noborder = true; // If the Motif hint is now telling us to show decorations, we only do so if the app didn't // instruct us to hide decorations in some other way, though. else if ( !motif_noborder && !app_noborder ) noborder = false; } if( !hasNETSupport() ) { // NETWM apps should set type and size constraints motif_may_resize = mresize; // This should be set using minsize==maxsize, but oh well motif_may_move = mmove; } else motif_may_resize = motif_may_move = true; // 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 if( isManaged() ) updateDecoration( true ); // Check if noborder state has changed } void Client::readIcons( Window win, QPixmap* icon, QPixmap* miniicon, QPixmap* bigicon, QPixmap* hugeicon ) { // Get the icons, allow scaling if( icon != NULL ) *icon = KWindowSystem::icon( win, 32, 32, true, KWindowSystem::NETWM | KWindowSystem::WMHints ); if( miniicon != NULL ) { if( icon == NULL || !icon->isNull() ) *miniicon = KWindowSystem::icon( win, 16, 16, true, KWindowSystem::NETWM | KWindowSystem::WMHints ); else *miniicon = QPixmap(); } if( bigicon != NULL ) { if( icon == NULL || !icon->isNull() ) *bigicon = KWindowSystem::icon( win, 64, 64, false, KWindowSystem::NETWM | KWindowSystem::WMHints ); else *bigicon = QPixmap(); } if( hugeicon != NULL ) { if( icon == NULL || !icon->isNull() ) *hugeicon = KWindowSystem::icon( win, 128, 128, false, KWindowSystem::NETWM | KWindowSystem::WMHints ); else *hugeicon = QPixmap(); } } void Client::getIcons() { // First read icons from the window itself readIcons( window(), &icon_pix, &miniicon_pix, &bigicon_pix, &hugeicon_pix ); if( icon_pix.isNull() ) { // Then try window group icon_pix = group()->icon(); miniicon_pix = group()->miniIcon(); bigicon_pix = group()->bigIcon(); hugeicon_pix = group()->hugeIcon(); } if( icon_pix.isNull() && isTransient() ) { // Then mainclients ClientList mainclients = mainClients(); for( ClientList::ConstIterator it = mainclients.constBegin(); it != mainclients.constEnd() && icon_pix.isNull(); ++it ) { icon_pix = (*it)->icon(); miniicon_pix = (*it)->miniIcon(); bigicon_pix = (*it)->bigIcon(); hugeicon_pix = (*it)->hugeIcon(); } } if( icon_pix.isNull()) { // And if nothing else, load icon from classhint or xapp icon icon_pix = KWindowSystem::icon( window(), 32, 32, true, KWindowSystem::ClassHint | KWindowSystem::XApp ); miniicon_pix = KWindowSystem::icon( window(), 16, 16, true, KWindowSystem::ClassHint | KWindowSystem::XApp ); bigicon_pix = KWindowSystem::icon( window(), 64, 64, false, KWindowSystem::ClassHint | KWindowSystem::XApp ); hugeicon_pix = KWindowSystem::icon( window(), 128, 128, false, KWindowSystem::ClassHint | KWindowSystem::XApp ); } if( isManaged() && decoration != NULL ) decoration->iconChange(); } QPixmap Client::icon( const QSize& size ) const { const int iconSize = qMin( size.width(), size.height() ); if( iconSize <= 16 ) return miniIcon(); else if( iconSize <= 32 ) return icon(); if( iconSize <= 64 ) return bigIcon(); else return hugeIcon(); } void Client::getWindowProtocols() { Atom* p; int i,n; Pdeletewindow = 0; Ptakefocus = 0; Ptakeactivity = 0; Pcontexthelp = 0; Pping = 0; if( XGetWMProtocols( display(), window(), &p, &n )) { for( i = 0; i < n; ++i ) { if( p[i] == atoms->wm_delete_window ) Pdeletewindow = 1; else if( p[i] == atoms->wm_take_focus ) Ptakefocus = 1; else if( p[i] == atoms->net_wm_take_activity ) Ptakeactivity = 1; else if( p[i] == atoms->net_wm_context_help ) Pcontexthelp = 1; else if( p[i] == atoms->net_wm_ping ) Pping = 1; } if( n > 0 ) XFree( p ); } } void Client::getSyncCounter() { #ifdef HAVE_XSYNC if( !Extensions::syncAvailable() ) return; Atom retType; unsigned long nItemRet; unsigned long byteRet; int formatRet; unsigned char* propRet; int ret = XGetWindowProperty( display(), window(), atoms->net_wm_sync_request_counter, 0, 1, false, XA_CARDINAL, &retType, &formatRet, &nItemRet, &byteRet, &propRet ); if( ret == Success && formatRet == 32 ) { sync_counter = *(long*)( propRet ); XSyncIntToValue( &sync_counter_value, 0 ); XSyncValue zero; XSyncIntToValue( &zero, 0 ); XSyncSetCounter( display(), sync_counter, zero ); if( sync_alarm == None ) { XSyncAlarmAttributes attrs; attrs.trigger.counter = sync_counter; attrs.trigger.value_type = XSyncRelative; attrs.trigger.test_type = XSyncPositiveTransition; XSyncIntToValue( &attrs.trigger.wait_value, 1 ); XSyncIntToValue( &attrs.delta, 1 ); sync_alarm = XSyncCreateAlarm( display(), XSyncCACounter | XSyncCAValueType | XSyncCATestType | XSyncCADelta | XSyncCAValue, &attrs ); } } if( ret == Success ) XFree( propRet ); #endif } /** * Send the client a _NET_SYNC_REQUEST */ void Client::sendSyncRequest() { #ifdef HAVE_XSYNC if( sync_counter == None ) return; // We increment before the notify so that after the notify // syncCounterSerial will equal the value we are expecting // in the acknowledgement int overflow; XSyncValue one; XSyncIntToValue( &one, 1 ); #undef XSyncValueAdd // It causes a warning :-/ XSyncValueAdd( &sync_counter_value, sync_counter_value, one, &overflow ); // Send the message to client XEvent ev; ev.xclient.type = ClientMessage; ev.xclient.window = window(); ev.xclient.format = 32; ev.xclient.message_type = atoms->wm_protocols; ev.xclient.data.l[0] = atoms->net_wm_sync_request; ev.xclient.data.l[1] = xTime(); ev.xclient.data.l[2] = XSyncValueLow32( sync_counter_value ); ev.xclient.data.l[3] = XSyncValueHigh32( sync_counter_value ); ev.xclient.data.l[4] = 0; XSendEvent( display(), window(), False, NoEventMask, &ev ); XSync( display(), false ); #endif } bool Client::wantsTabFocus() const { return ( isNormalWindow() || isDialog() ) && wantsInput(); } bool Client::wantsInput() const { return rules()->checkAcceptFocus( input || Ptakefocus ); } bool Client::isSpecialWindow() const { // TODO return isDesktop() || isDock() || isSplash() || isTopMenu() || isToolbar(); } /** * Sets an appropriate cursor shape for the logical mouse position \a m */ void Client::updateCursor() { Position m = mode; if( !isResizable() || isShade() ) m = PositionCenter; QCursor c; switch( m ) { case PositionTopLeft: case PositionBottomRight: c = Qt::SizeFDiagCursor; break; case PositionBottomLeft: case PositionTopRight: c = Qt::SizeBDiagCursor; break; case PositionTop: case PositionBottom: c = Qt::SizeVerCursor; break; case PositionLeft: case PositionRight: c = Qt::SizeHorCursor; break; default: if( moveResizeMode ) c = Qt::SizeAllCursor; else c = Qt::ArrowCursor; break; } if( c.handle() == cursor.handle()) return; cursor = c; if( decoration != NULL ) decoration->widget()->setCursor( cursor ); XDefineCursor( display(), frameId(), cursor.handle() ); if( moveResizeMode ) // XDefineCursor doesn't change cursor if there's pointer grab active XChangeActivePointerGrab( display(), ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask, cursor.handle(), xTime()); } Client::Position Client::mousePosition( const QPoint& p ) const { if( decoration != NULL ) return decoration->mousePosition( p ); return PositionCenter; } void Client::updateAllowedActions( bool force ) { if( !isManaged() && !force ) return; unsigned long old_allowed_actions = allowed_actions; allowed_actions = 0; if( isMovable() ) allowed_actions |= NET::ActionMove; if( isResizable() ) allowed_actions |= NET::ActionResize; if( isMinimizable() ) allowed_actions |= NET::ActionMinimize; if( isShadeable() ) allowed_actions |= NET::ActionShade; // Sticky state not supported if( isMaximizable() ) allowed_actions |= NET::ActionMax; if( userCanSetFullScreen() ) allowed_actions |= NET::ActionFullScreen; allowed_actions |= NET::ActionChangeDesktop; // Always (Pagers shouldn't show Docks etc.) if( isCloseable() ) allowed_actions |= NET::ActionClose; if( old_allowed_actions == allowed_actions ) return; // TODO: This could be delayed and compressed - It's only for pagers etc. anyway info->setAllowedActions( allowed_actions ); // TODO: This should also tell the decoration, so that it can update the buttons } void Client::autoRaise() { workspace()->raiseClient( this ); cancelAutoRaise(); } void Client::cancelAutoRaise() { delete autoRaiseTimer; autoRaiseTimer = 0; } void Client::debug( QDebug& stream ) const { stream << "\'ID:" << window() << ";WMCLASS:" << resourceClass() << ":" << resourceName() << ";Caption:" << caption() << "\'"; } QPixmap* kwin_get_menu_pix_hack() { static QPixmap p; if( p.isNull() ) p = SmallIcon( "bx2" ); return &p; } void Client::checkActivities() { QStringList newActivitiesList; QByteArray prop = getStringProperty(window(), atoms->activities); - if ( ! prop.isEmpty() ) - newActivitiesList = QString(prop).split(','); + activitiesDefined = !prop.isEmpty(); + if (prop == "ALL") { + //copied from setOnAllActivities to avoid a redundant XChangeProperty. + if (!activityList.isEmpty()) { + activityList.clear(); + updateActivities( true ); + } + return; + } + if (prop.isEmpty()) { + //note: this makes it *act* like it's on all activities but doesn't set the property to 'ALL' + if (!activityList.isEmpty()) { + activityList.clear(); + updateActivities( true ); + } + return; + } + newActivitiesList = QString(prop).split(','); if (newActivitiesList == activityList) return; //expected change, it's ok. //otherwise, somebody else changed it. we need to validate before reacting QStringList allActivities = workspace()->activityList(); if (allActivities.isEmpty()) { kDebug() << "no activities!?!?"; //don't touch anything, there's probably something bad going on and we don't wanna make it worse return; } for ( int i = 0; i < newActivitiesList.size(); ++i ) { if ( ! allActivities.contains( newActivitiesList.at(i) ) ) { kDebug() << "invalid:" << newActivitiesList.at(i); newActivitiesList.removeAt( i-- ); } } setOnActivities( newActivitiesList ); } +void Client::setSessionInteract(bool needed) +{ + needsSessionInteract = needed; +} + } // namespace #include "client.moc" diff --git a/client.h b/client.h index f8d67d337..d36ab6158 100644 --- a/client.h +++ b/client.h @@ -1,998 +1,1004 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_CLIENT_H #define KWIN_CLIENT_H #include #include #include #include #include #include #include #include #include #include #include #include #include "scripting/client.h" #include "utils.h" #include "options.h" #include "workspace.h" #include "kdecoration.h" #include "rules.h" #include "toplevel.h" #include "clientgroup.h" #ifdef HAVE_XSYNC #include #endif // TODO: Cleanup the order of things in this .h file class QProcess; class QTimer; class KStartupInfoData; namespace SWrapper { class Client; } typedef QPair ClientResolution; namespace KWin { namespace TabBox { class TabBoxClientImpl; } class Workspace; class Client; class Bridge; class PaintRedirector; class Client : public Toplevel { Q_OBJECT public: Client( Workspace* ws ); Window wrapperId() const; Window decorationId() const; const Client* transientFor() const; Client* transientFor(); bool isTransient() const; bool groupTransient() const; bool wasOriginallyGroupTransient() const; ClientList mainClients() const; // Call once before loop , is not indirect ClientList allMainClients() const; // Call once before loop , is indirect bool hasTransient( const Client* c, bool indirect ) const; const ClientList& transients() const; // Is not indirect void checkTransient( Window w ); Client* findModal( bool allow_itself = false ); const Group* group() const; Group* group(); void checkGroup( Group* gr = NULL, bool force = false ); void changeClientLeaderGroup( Group* gr ); const WindowRules* rules() const; void removeRule( Rules* r ); void setupWindowRules( bool ignore_temporary ); void applyWindowRules(); void updateWindowRules(); void updateFullscreenMonitors( NETFullscreenMonitors topology ); /** * Returns true for "special" windows and false for windows which are "normal" * (normal=window which has a border, can be moved by the user, can be closed, etc.) * true for Desktop, Dock, Splash, Override and TopMenu (and Toolbar??? - for now) * false for Normal, Dialog, Utility and Menu (and Toolbar??? - not yet) TODO */ bool isSpecialWindow() const; bool hasNETSupport() const; /** * This is a public object with no wrappers or anything to keep it fast, * so in essence, direct access is allowed. Please be very careful while * using this object */ QHash* scriptCache; QSize minSize() const; QSize maxSize() const; virtual QPoint clientPos() const; // Inside of geometry() virtual QSize clientSize() const; virtual QRect visibleRect() const; bool windowEvent( XEvent* e ); virtual bool eventFilter( QObject* o, QEvent* e ); #ifdef HAVE_XSYNC void syncEvent( XSyncAlarmNotifyEvent* e ); #endif bool manage( Window w, bool isMapped ); void releaseWindow( bool on_shutdown = false ); void destroyClient(); /// How to resize the window in order to obey constains (mainly aspect ratios) enum Sizemode { SizemodeAny, SizemodeFixedW, ///< Try not to affect width SizemodeFixedH, ///< Try not to affect height SizemodeMax ///< Try not to make it larger in either direction }; QSize adjustedSize( const QSize&, Sizemode mode = SizemodeAny ) const; QSize adjustedSize() const; QPixmap icon() const; QPixmap icon( const QSize& size ) const; QPixmap miniIcon() const; QPixmap bigIcon() const; QPixmap hugeIcon() const; bool isActive() const; void setActive( bool ); virtual int desktop() const; void setDesktop( int ); void setOnAllDesktops( bool set ); virtual QStringList activities() const; void setOnActivity( const QString &activity, bool enable ); void setOnAllActivities( bool set ); void setOnActivities( QStringList newActivitiesList ); void updateActivities( bool includeTransients ); /// Is not minimized and not hidden. I.e. normally visible on some virtual desktop. bool isShown( bool shaded_is_shown ) const; bool isHiddenInternal() const; // For compositing bool isShade() const; // True only for ShadeNormal ShadeMode shadeMode() const; // Prefer isShade() void setShade( ShadeMode mode ); bool isShadeable() const; bool isMinimized() const; bool isMaximizable() const; QRect geometryRestore() const; MaximizeMode maximizeModeRestore() const; MaximizeMode maximizeMode() const; bool isMinimizable() const; void setMaximize( bool vertically, bool horizontally ); QRect iconGeometry() const; void setFullScreen( bool set, bool user ); bool isFullScreen() const; bool isFullScreenable( bool fullscreen_hack = false ) const; bool isActiveFullScreen() const; bool userCanSetFullScreen() const; QRect geometryFSRestore() const { return geom_fs_restore; } // Only for session saving int fullScreenMode() const { return fullscreen_mode; } // only for session saving bool noBorder() const; void setNoBorder( bool set ); bool userCanSetNoBorder() const; void checkNoBorder(); bool skipTaskbar( bool from_outside = false ) const; void setSkipTaskbar( bool set, bool from_outside ); bool skipPager() const; void setSkipPager( bool ); bool skipSwitcher() const; void setSkipSwitcher( bool set ); bool keepAbove() const; void setKeepAbove( bool ); bool keepBelow() const; void setKeepBelow( bool ); Layer layer() const; Layer belongsToLayer() const; void invalidateLayer(); int sessionStackingOrder() const; void setModal( bool modal ); bool isModal() const; // Auxiliary functions, depend on the windowType bool wantsTabFocus() const; bool wantsInput() const; bool isResizable() const; bool isMovable() const; bool isMovableAcrossScreens() const; bool isCloseable() const; ///< May be closed by the user (May have a close button) void takeActivity( int flags, bool handled, allowed_t ); // Takes ActivityFlags as arg (in utils.h) void takeFocus( allowed_t ); void demandAttention( bool set = true ); void setMask( const QRegion& r, int mode = X::Unsorted ); QRegion mask() const; void updateDecoration( bool check_workspace_pos, bool force = false ); bool checkBorderSizes( bool also_resize ); void triggerDecorationRepaint(); void updateShape(); void setGeometry( int x, int y, int w, int h, ForceGeometry_t force = NormalGeometrySet, bool emitJs = true ); void setGeometry( const QRect& r, ForceGeometry_t force = NormalGeometrySet, bool emitJs = true ); void move( int x, int y, ForceGeometry_t force = NormalGeometrySet ); void move( const QPoint& p, ForceGeometry_t force = NormalGeometrySet ); /// plainResize() simply resizes void plainResize( int w, int h, ForceGeometry_t force = NormalGeometrySet, bool emitJs = true ); void plainResize( const QSize& s, ForceGeometry_t force = NormalGeometrySet, bool emitJs = true ); /// resizeWithChecks() resizes according to gravity, and checks workarea position void resizeWithChecks( int w, int h, ForceGeometry_t force = NormalGeometrySet ); void resizeWithChecks( const QSize& s, ForceGeometry_t force = NormalGeometrySet ); void keepInArea( QRect area, bool partial = false ); void setElectricBorderMode( ElectricMaximizingMode mode ); ElectricMaximizingMode electricBorderMode() const; void setElectricBorderMaximizing( bool maximizing ); bool isElectricBorderMaximizing() const; QRect electricBorderMaximizeGeometry(); QSize sizeForClientSize( const QSize&, Sizemode mode = SizemodeAny, bool noframe = false ) const; /** Set the quick tile mode ("snap") of this window. * This will also handle preserving and restoring of window geometry as necessary. * @param mode The tile mode (left/right) to give this window. */ void setQuickTileMode( QuickTileMode mode, bool keyboard = false ); void growHorizontal(); void shrinkHorizontal(); void growVertical(); void shrinkVertical(); bool providesContextHelp() const; KShortcut shortcut() const; void setShortcut( const QString& cut ); WindowOperation mouseButtonToWindowOperation( Qt::MouseButtons button ); bool performMouseCommand( Options::MouseCommand, const QPoint& globalPos, bool handled = false ); QRect adjustedClientArea( const QRect& desktop, const QRect& area ) const; Colormap colormap() const; /// Updates visibility depending on being shaded, virtual desktop, etc. void updateVisibility(); /// Hides a client - Basically like minimize, but without effects, it's simply hidden void hideClient( bool hide ); bool hiddenPreview() const; ///< Window is mapped in order to get a window pixmap virtual void setupCompositing(); virtual void finishCompositing(); QString caption( bool full = true ) const; void updateCaption(); void keyPressEvent( uint key_code ); // FRAME ?? void updateMouseGrab(); Window moveResizeGrabWindow() const; const QPoint calculateGravitation( bool invert, int gravity = 0 ) const; // FRAME public? void NETMoveResize( int x_root, int y_root, NET::Direction direction ); void NETMoveResizeWindow( int flags, int x, int y, int width, int height ); void restackWindow( Window above, int detail, NET::RequestSource source, Time timestamp, bool send_event = false ); void gotPing( Time timestamp ); void checkWorkspacePosition(); void updateUserTime( Time time = CurrentTime ); Time userTime() const; bool hasUserTimeSupport() const; bool ignoreFocusStealing() const; /// Does 'delete c;' static void deleteClient( Client* c, allowed_t ); static bool belongToSameApplication( const Client* c1, const Client* c2, bool active_hack = false ); static bool sameAppWindowRoleMatch( const Client* c1, const Client* c2, bool active_hack ); static void readIcons( Window win, QPixmap* icon, QPixmap* miniicon, QPixmap* bigicon, QPixmap* hugeicon ); void minimize( bool avoid_animation = false ); void unminimize( bool avoid_animation = false ); void closeWindow(); void killWindow(); void maximize( MaximizeMode ); void toggleShade(); void showContextHelp(); void cancelShadeHoverTimer(); void cancelAutoRaise(); void checkActiveModal(); StrutRect strutRect( StrutArea area ) const; StrutRects strutRects() const; bool hasStrut() const; // Tabbing functions ClientGroup* clientGroup() const; // Returns a pointer to client_group void setClientGroup( ClientGroup* group ); /* * If shown is true the client is mapped and raised, if false * the client is unmapped and hidden, this function is called * when the tabbing group of the client switches its visible * client. */ void setClientShown( bool shown ); /* * When a click is done in the decoration and it calls the group * to change the visible client it starts to move-resize the new * client, this function stops it. */ void dontMoveResize(); /** * Whether or not the window has a strut that expands through the invisible area of * an xinerama setup where the monitors are not the same resolution. */ bool hasOffscreenXineramaStrut() const; bool isMove() const { return moveResizeMode && mode == PositionCenter; } bool isResize() const { return moveResizeMode && mode != PositionCenter; } // Decorations <-> Effects const QPixmap *topDecoPixmap() const { return &decorationPixmapTop; } const QPixmap *leftDecoPixmap() const { return &decorationPixmapLeft; } const QPixmap *bottomDecoPixmap() const { return &decorationPixmapBottom; } const QPixmap *rightDecoPixmap() const { return &decorationPixmapRight; } int paddingLeft() const { return padding_left; } int paddingRight() const { return padding_right; } int paddingTop() const { return padding_top; } int paddingBottom() const { return padding_bottom; } bool decorationPixmapRequiresRepaint(); void ensureDecorationPixmapsPainted(); QRect decorationRect() const { return (decoration && decoration->widget()) ? decoration->widget()->rect().translated(-padding_left, -padding_top) : QRect(0, 0, width(), height()); } QRect transparentRect() const; QRegion decorationPendingRegion() const; enum CoordinateMode { DecorationRelative, // Relative to the top left corner of the decoration WindowRelative // Relative to the top left corner of the window }; void layoutDecorationRects(QRect &left, QRect &top, QRect &right, QRect &bottom, CoordinateMode mode) const; virtual void addRepaintFull(); TabBox::TabBoxClientImpl* tabBoxClient() const { return m_tabBoxClient; } + //sets whether the client should be treated as a SessionInteract window + void setSessionInteract(bool needed); + private slots: void autoRaise(); void shadeHover(); void shadeUnhover(); void shortcutActivated(); void delayedMoveResize(); private: friend class Bridge; // FRAME virtual void processMousePressEvent( QMouseEvent* e ); private: // Use Workspace::createClient() virtual ~Client(); ///< Use destroyClient() or releaseWindow() Position mousePosition( const QPoint& ) const; void updateCursor(); // Transparent stuff void drawbound( const QRect& geom ); void clearbound(); void doDrawbound( const QRect& geom, bool clear ); // Handlers for X11 events bool mapRequestEvent( XMapRequestEvent* e ); void unmapNotifyEvent( XUnmapEvent* e ); void destroyNotifyEvent( XDestroyWindowEvent* e ); void configureRequestEvent( XConfigureRequestEvent* e ); virtual void propertyNotifyEvent( XPropertyEvent* e ); void clientMessageEvent( XClientMessageEvent* e ); void enterNotifyEvent( XCrossingEvent* e ); void leaveNotifyEvent( XCrossingEvent* e ); void focusInEvent( XFocusInEvent* e ); void focusOutEvent( XFocusOutEvent* e ); #ifdef HAVE_XDAMAGE virtual void damageNotifyEvent( XDamageNotifyEvent* e ); #endif bool buttonPressEvent( Window w, int button, int state, int x, int y, int x_root, int y_root ); bool buttonReleaseEvent( Window w, int button, int state, int x, int y, int x_root, int y_root ); bool motionNotifyEvent( Window w, int state, int x, int y, int x_root, int y_root ); void checkQuickTilingMaximizationZones( int xroot, int yroot ); bool processDecorationButtonPress( int button, int state, int x, int y, int x_root, int y_root, bool ignoreMenu = false ); protected: virtual void debug( QDebug& stream ) const; virtual bool shouldUnredirect() const; private slots: void pingTimeout(); void processKillerExited(); void demandAttentionKNotify(); void syncTimeout(); void delayedSetShortcut(); void repaintDecorationPending(); //Signals for the scripting interface //Signals make an excellent way for communcation //in between objects as compared to simple function //calls signals: void s_clientMoved(); void clientManaging(KWin::Client*); void s_minimized(); void s_unminimized(); void maximizeSet(QPair); void s_activated(); void s_fullScreenSet(bool, bool); // To make workspace-client calls, a few slots are also // required public slots: void sl_activated(); private: void exportMappingState( int s ); // ICCCM 4.1.3.1, 4.1.4, NETWM 2.5.1 bool isManaged() const; ///< Returns false if this client is not yet managed void updateAllowedActions( bool force = false ); QRect fullscreenMonitorsArea( NETFullscreenMonitors topology ) const; void changeMaximize( bool horizontal, bool vertical, bool adjust ); void checkMaximizeGeometry(); int checkFullScreenHack( const QRect& geom ) const; // 0 - None, 1 - One xinerama screen, 2 - Full area void updateFullScreenHack( const QRect& geom ); void getWmNormalHints(); void getMotifHints(); void getIcons(); void fetchName(); void fetchIconicName(); QString readName() const; void setCaption( const QString& s, bool force = false ); bool hasTransientInternal( const Client* c, bool indirect, ConstClientList& set ) const; void finishWindowRules(); void setShortcutInternal( const KShortcut& cut ); void checkDirection( int new_diff, int old_diff, QRect& rect, const QRect& area ); void configureRequest( int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool ); NETExtendedStrut strut() const; int checkShadeGeometry( int w, int h ); void blockGeometryUpdates( bool block ); void getSyncCounter(); void sendSyncRequest(); bool startMoveResize(); void finishMoveResize( bool cancel ); void leaveMoveResize(); void checkUnrestrictedMoveResize(); void handleMoveResize( int x, int y, int x_root, int y_root ); void startDelayedMoveResize(); void stopDelayedMoveResize(); void positionGeometryTip(); void grabButton( int mod ); void ungrabButton( int mod ); void resetMaximize(); void resizeDecoration( const QSize& s ); void performMoveResize(); void pingWindow(); void killProcess( bool ask, Time timestamp = CurrentTime ); void updateUrgency(); static void sendClientMessage( Window w, Atom a, Atom protocol, long data1 = 0, long data2 = 0, long data3 = 0 ); void embedClient( Window w, const XWindowAttributes& attr ); void detectNoBorder(); void destroyDecoration(); void updateFrameExtents(); void internalShow( allowed_t ); void internalHide( allowed_t ); void internalKeep( allowed_t ); void map( allowed_t ); void unmap( allowed_t ); void updateHiddenPreview(); void updateInputShape(); void repaintDecorationPixmap( QPixmap& pix, const QRect& r, const QPixmap& src, QRegion reg ); void resizeDecorationPixmaps(); Time readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStartupInfoData* asn_data, bool session ) const; Time readUserCreationTime() const; void startupIdChanged(); Window client; Window wrapper; KDecoration* decoration; Bridge* bridge; int desk; QStringList activityList; bool buttonDown; bool moveResizeMode; bool move_faked_activity; Window move_resize_grab_window; bool move_resize_has_keyboard_grab; bool unrestrictedMoveResize; Position mode; QPoint moveOffset; QPoint invertedMoveOffset; QRect moveResizeGeom; QRect initialMoveResizeGeom; XSizeHints xSizeHint; void sendSyntheticConfigureNotify(); enum MappingState { Withdrawn, ///< Not handled, as per ICCCM WithdrawnState Mapped, ///< The frame is mapped Unmapped, ///< The frame is not mapped Kept ///< The frame should be unmapped, but is kept (For compositing) }; MappingState mapping_state; /** The quick tile mode of this window. */ QuickTileMode quick_tile_mode; QRect geom_pretile; void readTransient(); Window verifyTransientFor( Window transient_for, bool set ); void addTransient( Client* cl ); void removeTransient( Client* cl ); void removeFromMainClients(); void cleanGrouping(); void checkGroupTransients(); void setTransient( Window new_transient_for_id ); Client* transient_for; Window transient_for_id; Window original_transient_for_id; ClientList transients_list; // SELI TODO: Make this ordered in stacking order? ShadeMode shade_mode; uint active : 1; uint deleting : 1; ///< True when doing cleanup and destroying the client uint keep_above : 1; ///< NET::KeepAbove (was stays_on_top) uint skip_taskbar : 1; uint original_skip_taskbar : 1; ///< Unaffected by KWin uint Pdeletewindow : 1; ///< Does the window understand the DeleteWindow protocol? uint Ptakefocus : 1;///< Does the window understand the TakeFocus protocol? uint Ptakeactivity : 1; ///< Does it support _NET_WM_TAKE_ACTIVITY uint Pcontexthelp : 1; ///< Does the window understand the ContextHelp protocol? uint Pping : 1; ///< Does it support _NET_WM_PING? uint input : 1; ///< Does the window want input in its wm_hints uint skip_pager : 1; uint skip_switcher : 1; uint motif_may_resize : 1; uint motif_may_move : 1; uint motif_may_close : 1; uint keep_below : 1; ///< NET::KeepBelow uint minimized : 1; uint hidden : 1; ///< Forcibly hidden by calling hide() uint modal : 1; ///< NET::Modal uint noborder : 1; uint app_noborder : 1; ///< App requested no border via window type, shape extension, etc. uint motif_noborder : 1; ///< App requested no border via Motif WM hints uint urgency : 1; ///< XWMHints, UrgencyHint uint ignore_focus_stealing : 1; ///< Don't apply focus stealing prevention to this client uint demands_attention : 1; WindowRules client_rules; void getWMHints(); void readIcons(); void getWindowProtocols(); QPixmap icon_pix; QPixmap miniicon_pix; QPixmap bigicon_pix; QPixmap hugeicon_pix; QCursor cursor; // DON'T reorder - Saved to config files !!! enum FullScreenMode { FullScreenNone, FullScreenNormal, FullScreenHack ///< Non-NETWM fullscreen (noborder and size of desktop) }; FullScreenMode fullscreen_mode; MaximizeMode max_mode; QRect geom_restore; QRect geom_fs_restore; MaximizeMode maxmode_restore; QTimer* autoRaiseTimer; QTimer* shadeHoverTimer; QTimer* delayedMoveResizeTimer; Colormap cmap; QString cap_normal, cap_iconic, cap_suffix; Group* in_group; Window window_group; ClientGroup* client_group; Layer in_layer; QTimer* ping_timer; QProcess* process_killer; Time ping_timestamp; Time user_time; unsigned long allowed_actions; QSize client_size; int block_geometry_updates; // > 0 = New geometry is remembered, but not actually set enum PendingGeometry_t { PendingGeometryNone, PendingGeometryNormal, PendingGeometryForced }; PendingGeometry_t pending_geometry_update; QRect geom_before_block; QRect deco_rect_before_block; bool shade_geometry_change; #ifdef HAVE_XSYNC XSyncCounter sync_counter; XSyncValue sync_counter_value; XSyncAlarm sync_alarm; #endif QTimer* sync_timeout; bool sync_resize_pending; int border_left, border_right, border_top, border_bottom; int padding_left, padding_right, padding_top, padding_bottom; QRegion _mask; static bool check_active_modal; ///< \see Client::checkActiveModal() KShortcut _shortcut; int sm_stacking_order; friend struct FetchNameInternalPredicate; friend struct CheckIgnoreFocusStealingProcedure; friend struct ResetupRulesProcedure; friend class GeometryUpdatesBlocker; QTimer* demandAttentionKNotifyTimer; QPixmap decorationPixmapLeft, decorationPixmapRight, decorationPixmapTop, decorationPixmapBottom; PaintRedirector* paintRedirector; TabBox::TabBoxClientImpl* m_tabBoxClient; bool electricMaximizing; ElectricMaximizingMode electricMode; friend bool performTransiencyCheck(); friend class SWrapper::Client; void checkActivities(); + bool activitiesDefined; //whether the x property was actually set + + bool needsSessionInteract; }; /** * Helper for Client::blockGeometryUpdates() being called in pairs (true/false) */ class GeometryUpdatesBlocker { public: GeometryUpdatesBlocker( Client* c ) : cl( c ) { cl->blockGeometryUpdates( true ); } ~GeometryUpdatesBlocker() { cl->blockGeometryUpdates( false ); } private: Client* cl; }; /** * NET WM Protocol handler class */ class WinInfo : public NETWinInfo2 { private: typedef KWin::Client Client; // Because of NET::Client public: WinInfo( Client* c, Display * display, Window window, Window rwin, const unsigned long pr[], int pr_size ); virtual void changeDesktop(int desktop); virtual void changeFullscreenMonitors(NETFullscreenMonitors topology); virtual void changeState( unsigned long state, unsigned long mask ); void disable(); private: Client * m_client; }; inline Window Client::wrapperId() const { return wrapper; } inline Window Client::decorationId() const { return decoration != NULL ? decoration->widget()->winId() : None; } inline const Client* Client::transientFor() const { return transient_for; } inline Client* Client::transientFor() { return transient_for; } inline bool Client::groupTransient() const { return transient_for_id == rootWindow(); } // Needed because verifyTransientFor() may set transient_for_id to root window, // if the original value has a problem (window doesn't exist, etc.) inline bool Client::wasOriginallyGroupTransient() const { return original_transient_for_id == rootWindow(); } inline bool Client::isTransient() const { return transient_for_id != None; } inline const ClientList& Client::transients() const { return transients_list; } inline const Group* Client::group() const { return in_group; } inline Group* Client::group() { return in_group; } inline void Client::setClientGroup( ClientGroup* group ) { client_group = group; } inline ClientGroup* Client::clientGroup() const { return client_group; } inline bool Client::isMinimized() const { return minimized; } inline bool Client::isActive() const { return active; } inline bool Client::isShown( bool shaded_is_shown ) const { return !isMinimized() && ( !isShade() || shaded_is_shown ) && !hidden && ( clientGroup() == NULL || clientGroup()->visible() == this ); } inline bool Client::isHiddenInternal() const { return hidden; } inline bool Client::isShade() const { return shade_mode == ShadeNormal; } inline ShadeMode Client::shadeMode() const { return shade_mode; } inline QPixmap Client::icon() const { return icon_pix; } inline QPixmap Client::miniIcon() const { return miniicon_pix; } inline QPixmap Client::bigIcon() const { return bigicon_pix; } inline QPixmap Client::hugeIcon() const { return hugeicon_pix; } inline QRect Client::geometryRestore() const { return geom_restore; } inline Client::MaximizeMode Client::maximizeModeRestore() const { return maxmode_restore; } inline Client::MaximizeMode Client::maximizeMode() const { return max_mode; } inline bool Client::skipTaskbar( bool from_outside ) const { return from_outside ? original_skip_taskbar : skip_taskbar; } inline bool Client::skipPager() const { return skip_pager; } inline bool Client::skipSwitcher() const { return skip_switcher; } inline bool Client::keepAbove() const { return keep_above; } inline bool Client::keepBelow() const { return keep_below; } inline bool Client::isFullScreen() const { return fullscreen_mode != FullScreenNone; } inline bool Client::isModal() const { return modal; } inline bool Client::hasNETSupport() const { return info->hasNETSupport(); } inline Colormap Client::colormap() const { return cmap; } inline void Client::invalidateLayer() { in_layer = UnknownLayer; } inline int Client::sessionStackingOrder() const { return sm_stacking_order; } inline bool Client::isManaged() const { return mapping_state != Withdrawn; } inline QPoint Client::clientPos() const { return QPoint( border_left, border_top ); } inline QSize Client::clientSize() const { return client_size; } inline QRect Client::visibleRect() const { return geometry().adjusted( -padding_left, -padding_top, padding_right, padding_bottom ); } inline void Client::setGeometry( const QRect& r, ForceGeometry_t force, bool emitJs ) { setGeometry( r.x(), r.y(), r.width(), r.height(), force, emitJs ); } inline void Client::move( const QPoint& p, ForceGeometry_t force ) { move( p.x(), p.y(), force ); } inline void Client::plainResize( const QSize& s, ForceGeometry_t force, bool emitJs ) { plainResize( s.width(), s.height(), force, emitJs ); } inline void Client::resizeWithChecks( const QSize& s, ForceGeometry_t force ) { resizeWithChecks( s.width(), s.height(), force ); } inline bool Client::hasUserTimeSupport() const { return info->userTime() != -1U; } inline bool Client::ignoreFocusStealing() const { return ignore_focus_stealing; } inline const WindowRules* Client::rules() const { return &client_rules; } KWIN_PROCEDURE( CheckIgnoreFocusStealingProcedure, Client, cl->ignore_focus_stealing = options->checkIgnoreFocusStealing( cl )); inline Window Client::moveResizeGrabWindow() const { return move_resize_grab_window; } inline KShortcut Client::shortcut() const { return _shortcut; } inline void Client::removeRule( Rules* rule ) { client_rules.remove( rule ); } inline bool Client::hiddenPreview() const { return mapping_state == Kept; } KWIN_COMPARE_PREDICATE( WrapperIdMatchPredicate, Client, Window, cl->wrapperId() == value ); } // namespace #endif diff --git a/manage.cpp b/manage.cpp index fbb531dad..cd1e75dc9 100644 --- a/manage.cpp +++ b/manage.cpp @@ -1,673 +1,696 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ // This file contains things relevant to handling incoming events. #include "client.h" #include #include #include #include "notifications.h" #include #include "rules.h" #include "group.h" #include "scripting/workspaceproxy.h" namespace KWin { /** * 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 ) { StackingUpdatesBlocker stacking_blocker( workspace() ); //Scripting call. Does not use a signal/slot mechanism //as ensuring connections was a bit difficult between //so many clients and the workspace SWrapper::WorkspaceProxy* ws_wrap = SWrapper::WorkspaceProxy::instance(); if(ws_wrap != 0) { ws_wrap->sl_clientManaging(this); } grabXServer(); XWindowAttributes attr; if( !XGetWindowAttributes( display(), w, &attr )) { ungrabXServer(); return false; } // From this place on, manage() must not return false block_geometry_updates = 1; pending_geometry_update = PendingGeometryForced; // Force update when finishing with geometry changes embedClient( w, attr ); vis = attr.visual; bit_depth = attr.depth; // SELI TODO: Order all these things in some sane manner bool init_minimize = false; XWMHints* hints = XGetWMHints( display(), 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 | NET::WM2Opacity | NET::WM2FullscreenMonitors | NET::WM2FrameOverlap | 0; info = new WinInfo( this, display(), client, rootWindow(), properties, 2 ); cmap = attr.colormap; getResourceClass(); getWindowRole(); getWmClientLeader(); getWmClientMachine(); getSyncCounter(); // 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 ); ignore_focus_stealing = options->checkIgnoreFocusStealing( this ); // TODO: Change to rules setCaption( cap_normal, true ); if( Extensions::shapeAvailable() ) XShapeSelectInput( display(), window(), ShapeNotifyMask ); detectShape( window() ); detectNoBorder(); 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; setupCompositing(); KStartupInfoId asn_id; KStartupInfoData asn_data; bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data ); workspace()->updateClientLayer( this ); SessionInfo* session = workspace()->takeSessionInfo( this ); if( session ) { init_minimize = session->minimized; noborder = session->noBorder; } setShortcut( rules()->checkShortcut( session ? session->shortcut : QString(), true )); init_minimize = rules()->checkMinimize( init_minimize, !isMapped ); noborder = rules()->checkNoBorder( noborder, !isMapped ); checkActivities(); // Initial desktop placement if( session ) { desk = session->desktop; if( session->onAllDesktops ) desk = NET::OnAllDesktops; setOnActivities(session->activities); } 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; bool on_all = false; Client* maincl = NULL; // This is slightly duplicated from Placement::placeOnMainWindow() for( ClientList::ConstIterator it = mainclients.constBegin(); it != mainclients.constEnd(); ++it ) { if( mainclients.count() > 1 && (*it)->isSpecialWindow() ) continue; // Don't consider toolbars etc when placing maincl = *it; if( (*it)->isOnCurrentDesktop() ) on_current = true; if( (*it)->isOnAllDesktops() ) on_all = true; } if( on_all ) desk = NET::OnAllDesktops; else if( on_current ) desk = workspace()->currentDesktop(); else if( maincl != NULL ) desk = maincl->desktop(); if ( maincl ) setOnActivities(maincl->activities()); } 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 (!isMapped && !noborder && isNormalWindow() && isOnAllActivities()) { + if (!isMapped && !noborder && isNormalWindow() && !activitiesDefined) { //a new, regular window, when we're not recovering from a crash, //and it hasn't got an activity. let's try giving it the current one. //TODO: decide whether to keep this before the 4.6 release //TODO: if we are keeping it (at least as an option), replace noborder checking //with a public API for setting windows to be on all activities. //something like KWindowSystem::setOnAllActivities or //KActivityConsumer::setOnAllActivities setOnActivity(Workspace::self()->currentActivity(), true); } } 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 = qMax( 1, qMin( workspace()->numberOfDesktops(), desk )); info->setDesktop( desk ); workspace()->updateOnAllDesktopsOfTransients( this ); // SELI TODO //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; 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, cursorPos(), 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() ) // KWin doesn't manage desktop windows 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 // #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, and now // there's also _NET_WM_FULL_PLACEMENT. 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( QString::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( QSize( xSizeHint.max_width, xSizeHint.max_height )))); if( xSizeHint.flags & PMinSize ) geom.setSize( geom.size().expandedTo( rules()->checkMinSize( QSize( xSizeHint.min_width, xSizeHint.min_height )))); if( isMovable() && ( geom.x() > area.right() || geom.y() > area.bottom() )) placementDone = false; // Weird, do not trust. if( placementDone ) move( geom.x(), geom.y() ); // Before gravitating // Create client group if the window will have a decoration bool dontKeepInArea = false; if( !noBorder() ) { client_group = NULL; // Automatically add to previous groups on session restore if( session && session->clientGroupClient && session->clientGroupClient != this ) session->clientGroupClient->clientGroup()->add( this, -1, true ); else if( isMapped ) // If the window is already mapped (Restarted KWin) add any windows that already have the // same geometry to the same client group. (May incorrectly handle maximized windows) foreach( ClientGroup* group, workspace()->clientGroups ) if( geom == QRect( group->visible()->pos(), group->visible()->clientSize() ) && desk == group->visible()->desktop() && activities() == group->visible()->activities() && group->visible()->maximizeMode() != MaximizeFull ) { group->add( this, -1, true ); break; } if( !client_group && !isMapped && !session ) { // Attempt to automatically group similar windows const Client* similar = workspace()->findSimilarClient( this ); if( similar && similar->clientGroup() && !similar->noBorder() ) { geom = QRect( similar->pos() + similar->clientPos(), similar->clientSize() ); updateDecoration( false ); similar->clientGroup()->add( this, -1, rules()->checkAutogroupInForeground( options->autogroupInForeground )); // Don't move entire group geom = QRect( similar->pos() + similar->clientPos(), similar->clientSize() ); placementDone = true; dontKeepInArea = true; } } if( !client_group ) client_group = new ClientGroup( this ); } updateDecoration( false ); // Also gravitates // TODO: Is CentralGravity right here, when resizing is done after gravitating? plainResize( rules()->checkSize( sizeForClientSize( geom.size() ), !isMapped )); QPoint 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() && !dontKeepInArea ) keepInArea( area, partial_keep_in_area ); 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.constBegin(); it != mainclients.constEnd(); ++it ) if( (*it)->isShown( true )) init_minimize = false; // SELI TODO: 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; // Use allMainClients(), to include also main clients of group transients // that have been optimized out in Client::checkGroupTransients() ClientList mainclients = allMainClients(); for( ClientList::ConstIterator it = mainclients.constBegin(); it != mainclients.constEnd(); ++it ) if( (*it)->isShown( true )) visible_parent = true; if( !visible_parent ) { init_minimize = true; demandAttention(); } } if( init_minimize ) minimize( true ); // No animation // SELI TODO: 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 ); setSkipSwitcher( session->skipSwitcher ); setShade( session->shaded ? ShadeNormal : ShadeNone ); setOpacity( session->opacity ); 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 MaximizeMode maxmode = static_cast( (( 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 )); setSkipSwitcher( rules()->checkSkipSwitcher( false, !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 ); // 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 // This should avoid flicker, because real restacking is done // only after manage() finishes because of blocking, but the window is shown sooner XLowerWindow( display(), frameId() ); if( session && session->stackingOrder != -1 ) { sm_stacking_order = session->stackingOrder; workspace()->restoreSessionStackingOrder( this ); } if( compositing() ) // Sending ConfigureNotify is done when setting mapping state below, // Getting the first sync response means window is ready for compositing sendSyncRequest(); 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() ); + if (!(isMapped || session)) { + if (workspace()->sessionSaving()) { + /* + * If we get a new window during session saving, we assume it's some 'save file?' dialog + * which the user really needs to see (to know why logout's stalled). + * We also assume it'll be destroyed when it's done - we never unset this flag. + * + * Given the current session management protocol, I can't see a nicer way of doing this. + * Someday I'd like to see a protocol that tells the windowmanager who's doing SessionInteract. + */ + needsSessionInteract = true; + //show the parent too + ClientList mainclients = mainClients(); + for( ClientList::ConstIterator it = mainclients.constBegin(); + it != mainclients.constEnd(); ++it ) { + (*it)->setSessionInteract(true); + } + } else if (allow) { + // also force if activation is allowed + if( !isOnCurrentDesktop() ) { + workspace()->setCurrentDesktop( desktop() ); + } + /*if (!isOnCurrentActivity()) { + workspace()->setCurrentActivity( activities().first() ); + } FIXME no such method*/ + } + } bool belongs_to_desktop = false; for( ClientList::ConstIterator it = group()->members().constBegin(); it != group()->members().constEnd(); ++it ) if( (*it)->isDesktop() ) { belongs_to_desktop = true; break; } if( !belongs_to_desktop && workspace()->showingDesktop() ) workspace()->resetShowingDesktop( options->showDesktopIsMinimizeAll ); if( isOnCurrentDesktop() && !isMapped && !allow && ( !session || session->stackingOrder < 0 )) workspace()->restackClientUnderActive( 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 hideClient( true ); // SELI HACK !!! assert( mapping_state != Withdrawn ); blockGeometryUpdates( false ); if( user_time == CurrentTime || user_time == -1U ) { // No known user time, set something old user_time = xTime() - 1000000; if( user_time == CurrentTime || user_time == -1U ) // Let's be paranoid user_time = xTime() - 1000000 + 10; } //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() // 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( frameId() == None ); assert( wrapper == None ); client = w; // We don't want the window to be destroyed when we are destroyed XAddToSaveSet( display(), client ); XSelectInput( display(), client, NoEventMask ); XUnmapWindow( display(), 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( display(), client, CWBorderWidth, &wc ); XSetWindowAttributes swa; swa.colormap = attr.colormap; swa.background_pixmap = None; swa.border_pixel = 0; Window frame = XCreateWindow( display(), rootWindow(), 0, 0, 1, 1, 0, attr.depth, InputOutput, attr.visual, CWColormap | CWBackPixmap | CWBorderPixel, &swa ); setWindowHandles( client, frame ); wrapper = XCreateWindow( display(), frame, 0, 0, 1, 1, 0, attr.depth, InputOutput, attr.visual, CWColormap | CWBackPixmap | CWBorderPixel, &swa ); XDefineCursor( display(), frame, QCursor( Qt::ArrowCursor ).handle() ); // Some apps are stupid and don't define their own cursor - set the arrow one for them XDefineCursor( display(), wrapper, QCursor( Qt::ArrowCursor ).handle() ); XReparentWindow( display(), client, wrapper, 0, 0 ); XSelectInput( display(), frame, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | KeymapStateMask | ButtonMotionMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask | ExposureMask | PropertyChangeMask | StructureNotifyMask | SubstructureRedirectMask ); XSelectInput( display(), wrapper, ClientWinMask | SubstructureNotifyMask ); XSelectInput( display(), client, FocusChangeMask | PropertyChangeMask | ColormapChangeMask | EnterWindowMask | LeaveWindowMask | KeyPressMask | KeyReleaseMask ); updateMouseGrab(); } } // namespace diff --git a/sm.cpp b/sm.cpp index ad3b5e232..163fd403d 100644 --- a/sm.cpp +++ b/sm.cpp @@ -1,618 +1,628 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "sm.h" #include #include #include #include #include #include #include "workspace.h" #include "client.h" #include #include #include #include namespace KWin { bool SessionManager::saveState( QSessionManager& sm ) { // If the session manager is ksmserver, save stacking // order, active window, active desktop etc. in phase 1, // as ksmserver assures no interaction will be done // before the WM finishes phase 1. Saving in phase 2 is // too late, as possible user interaction may change some things. // Phase2 is still needed though (ICCCM 5.2) char* sm_vendor = SmcVendor( static_cast< SmcConn >( sm.handle())); bool ksmserver = qstrcmp( sm_vendor, "KDE" ) == 0; free( sm_vendor ); if ( !sm.isPhase2() ) { Workspace::self()->sessionSaveStarted(); if( ksmserver ) // save stacking order etc. before "save file?" etc. dialogs change it Workspace::self()->storeSession( kapp->sessionConfig(), SMSavePhase0 ); sm.release(); // Qt doesn't automatically release in this case (bug?) sm.requestPhase2(); return true; } Workspace::self()->storeSession( kapp->sessionConfig(), ksmserver ? SMSavePhase2 : SMSavePhase2Full ); kapp->sessionConfig()->sync(); return true; } // I bet this is broken, just like everywhere else in KDE bool SessionManager::commitData( QSessionManager& sm ) { if ( !sm.isPhase2() ) Workspace::self()->sessionSaveStarted(); return true; } // Workspace /*! Stores the current session in the config file \sa loadSessionInfo() */ void Workspace::storeSession( KConfig* config, SMSavePhase phase ) { KConfigGroup cg(config, "Session"); int count = 0; int active_client = -1; if( phase == SMSavePhase2 || phase == SMSavePhase2Full ) { cg.writeEntry( "tiling", tilingEnabled() ); if( tilingEnabled() ) { kDebug(1212) << "Tiling was ON"; setTilingEnabled( false ); } } for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { Client* c = (*it); QByteArray sessionId = c->sessionId(); QByteArray wmCommand = c->wmCommand(); if ( sessionId.isEmpty() ) // remember also applications that are not XSMP capable // and use the obsolete WM_COMMAND / WM_SAVE_YOURSELF if ( wmCommand.isEmpty() ) continue; count++; if( c->isActive()) active_client = count; if( phase == SMSavePhase2 || phase == SMSavePhase2Full ) storeClient(cg, count, c); } if( phase == SMSavePhase0 ) { // it would be much simpler to save these values to the config file, // but both Qt and KDE treat phase1 and phase2 separately, // which results in different sessionkey and different config file :( session_active_client = active_client; session_desktop = currentDesktop(); } else if( phase == SMSavePhase2 ) { cg.writeEntry( "count", count ); cg.writeEntry( "active", session_active_client ); cg.writeEntry( "desktop", session_desktop ); } else // SMSavePhase2Full { cg.writeEntry( "count", count ); cg.writeEntry( "active", session_active_client ); cg.writeEntry( "desktop", currentDesktop()); } } void Workspace::storeClient( KConfigGroup &cg, int num, Client *c ) { + c->setSessionInteract(false); //make sure we get the real values QString n = QString::number(num); cg.writeEntry( QString("sessionId")+n, c->sessionId().constData() ); cg.writeEntry( QString("windowRole")+n, c->windowRole().constData() ); cg.writeEntry( QString("wmCommand")+n, c->wmCommand().constData() ); cg.writeEntry( QString("wmClientMachine")+n, c->wmClientMachine( true ).constData() ); cg.writeEntry( QString("resourceName")+n, c->resourceName().constData() ); cg.writeEntry( QString("resourceClass")+n, c->resourceClass().constData() ); cg.writeEntry( QString("geometry")+n, QRect( c->calculateGravitation(true), c->clientSize() ) ); // FRAME cg.writeEntry( QString("restore")+n, c->geometryRestore() ); cg.writeEntry( QString("fsrestore")+n, c->geometryFSRestore() ); cg.writeEntry( QString("maximize")+n, (int) c->maximizeMode() ); cg.writeEntry( QString("fullscreen")+n, (int) c->fullScreenMode() ); cg.writeEntry( QString("desktop")+n, c->desktop() ); // the config entry is called "iconified" for back. comp. reasons // (kconf_update script for updating session files would be too complicated) cg.writeEntry( QString("iconified")+n, c->isMinimized() ); cg.writeEntry( QString("opacity")+n, c->opacity() ); // the config entry is called "sticky" for back. comp. reasons cg.writeEntry( QString("sticky")+n, c->isOnAllDesktops() ); cg.writeEntry( QString("shaded")+n, c->isShade() ); // the config entry is called "staysOnTop" for back. comp. reasons cg.writeEntry( QString("staysOnTop")+n, c->keepAbove() ); cg.writeEntry( QString("keepBelow")+n, c->keepBelow() ); cg.writeEntry( QString("skipTaskbar")+n, c->skipTaskbar( true ) ); cg.writeEntry( QString("skipPager")+n, c->skipPager() ); cg.writeEntry( QString("skipSwitcher")+n, c->skipSwitcher() ); // not really just set by user, but name kept for back. comp. reasons cg.writeEntry( QString("userNoBorder")+n, c->noBorder() ); cg.writeEntry( QString("windowType")+n, windowTypeToTxt( c->windowType())); cg.writeEntry( QString("shortcut")+n, c->shortcut().toString()); cg.writeEntry( QString("stackingOrder")+n, unconstrained_stacking_order.indexOf( c )); int group = 0; if( c->clientGroup() ) group = c->clientGroup()->clients().count() > 1 ? // KConfig doesn't support long so we need to live with less precision on 64-bit systems static_cast( reinterpret_cast( c->clientGroup() )) : 0; cg.writeEntry( QString("clientGroup")+n, group ); cg.writeEntry( QString("activities")+n, c->activities() ); } void Workspace::storeSubSession(const QString &name, QSet sessionIds) { //TODO clear it first KConfigGroup cg(KGlobal::config(), QString("SubSession: ") + name); int count = 0; int active_client = -1; for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { Client* c = (*it); QByteArray sessionId = c->sessionId(); QByteArray wmCommand = c->wmCommand(); if ( sessionId.isEmpty() ) // remember also applications that are not XSMP capable // and use the obsolete WM_COMMAND / WM_SAVE_YOURSELF if ( wmCommand.isEmpty() ) continue; if (!sessionIds.contains(sessionId)) continue; kDebug() << "storing" << sessionId; count++; if( c->isActive()) active_client = count; storeClient(cg, count, c); } cg.writeEntry( "count", count ); cg.writeEntry( "active", active_client ); //cg.writeEntry( "desktop", currentDesktop()); } void Workspace::stopActivity(const QString &id) { //ugly hack to avoid dbus deadlocks QMetaObject::invokeMethod(this, "reallyStopActivity", Qt::QueuedConnection, Q_ARG(QString, id)); } void Workspace::reallyStopActivity(const QString &id) { QStringList openActivities = openActivityList(); QSet saveSessionIds; QSet dontCloseSessionIds; kDebug() << id; for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { Client* c = (*it); QByteArray sessionId = c->sessionId(); if ( sessionId.isEmpty() ) continue; //TODO support old wm_command apps too? kDebug() << sessionId; //if it's on the activity that's closing, it needs saving //but if a process is on some other open activity, I don't wanna close it yet //this is, of course, complicated by a process having many windows. if (c->isOnAllActivities()) { dontCloseSessionIds << sessionId; continue; } QStringList activities = c->activities(); foreach (const QString &activityId, activities) { if (activityId == id) saveSessionIds << sessionId; else if (openActivities.contains(activityId)) dontCloseSessionIds << sessionId; } } storeSubSession(id, saveSessionIds); QStringList saveAndClose; QStringList saveOnly; foreach (const QByteArray &sessionId, saveSessionIds) { if (dontCloseSessionIds.contains(sessionId)) saveOnly << sessionId; else saveAndClose << sessionId; } kDebug() << "saveActivity" << id << saveAndClose << saveOnly; //pass off to ksmserver QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface"); if (ksmserver.isValid()) { QDBusMessage reply = ksmserver.call("saveSubSession", id, saveAndClose, saveOnly); if (reply.type() == QDBusMessage::ErrorMessage) kDebug() << "dbus error:" << reply.errorMessage(); else kDebug() << "dbus succeeded"; } else kDebug() << "couldn't get ksmserver interface"; } /*! Loads the session information from the config file. \sa storeSession() */ void Workspace::loadSessionInfo() { session.clear(); KConfigGroup cg(kapp->sessionConfig(), "Session"); setTilingEnabled( cg.readEntry( "tiling", false ) ); addSessionInfo(cg); } void Workspace::addSessionInfo(KConfigGroup &cg) { int count = cg.readEntry( "count",0 ); int active_client = cg.readEntry( "active",0 ); for ( int i = 1; i <= count; i++ ) { QString n = QString::number(i); SessionInfo* info = new SessionInfo; session.append( info ); info->sessionId = cg.readEntry( QString("sessionId")+n, QString() ).toLatin1(); info->windowRole = cg.readEntry( QString("windowRole")+n, QString() ).toLatin1(); info->wmCommand = cg.readEntry( QString("wmCommand")+n, QString() ).toLatin1(); info->wmClientMachine = cg.readEntry( QString("wmClientMachine")+n, QString() ).toLatin1(); info->resourceName = cg.readEntry( QString("resourceName")+n, QString() ).toLatin1(); info->resourceClass = cg.readEntry( QString("resourceClass")+n, QString() ).toLower().toLatin1(); info->geometry = cg.readEntry( QString("geometry")+n,QRect() ); info->restore = cg.readEntry( QString("restore")+n,QRect() ); info->fsrestore = cg.readEntry( QString("fsrestore")+n,QRect() ); info->maximized = cg.readEntry( QString("maximize")+n, 0 ); info->fullscreen = cg.readEntry( QString("fullscreen")+n, 0 ); info->desktop = cg.readEntry( QString("desktop")+n, 0 ); info->minimized = cg.readEntry( QString("iconified")+n, false ); info->opacity = cg.readEntry( QString("opacity")+n, 1.0 ); info->onAllDesktops = cg.readEntry( QString("sticky")+n, false ); info->shaded = cg.readEntry( QString("shaded")+n, false ); info->keepAbove = cg.readEntry( QString("staysOnTop")+n, false ); info->keepBelow = cg.readEntry( QString("keepBelow")+n, false ); info->skipTaskbar = cg.readEntry( QString("skipTaskbar")+n, false ); info->skipPager = cg.readEntry( QString("skipPager")+n, false ); info->skipSwitcher = cg.readEntry( QString("skipSwitcher")+n, false ); info->noBorder = cg.readEntry( QString("userNoBorder")+n, false ); info->windowType = txtToWindowType( cg.readEntry( QString("windowType")+n, QString() ).toLatin1()); info->shortcut = cg.readEntry( QString("shortcut")+n, QString() ); info->active = ( active_client == i ); info->stackingOrder = cg.readEntry( QString("stackingOrder")+n, -1 ); info->clientGroup = cg.readEntry( QString("clientGroup")+n, 0 ); info->clientGroupClient = NULL; info->activities = cg.readEntry( QString("activities")+n, QStringList() ); } } void Workspace::loadSubSessionInfo(const QString &name) { KConfigGroup cg(KGlobal::config(), QString("SubSession: ") + name); addSessionInfo(cg); } void Workspace::startActivity(const QString &id) { if (!allActivities_.contains(id)) return; //bogus id loadSubSessionInfo(id); QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface"); if (ksmserver.isValid()) { QDBusMessage reply = ksmserver.call("restoreSubSession", id); if (reply.type() == QDBusMessage::ErrorMessage) kDebug() << "dbus error:" << reply.errorMessage(); else kDebug() << "dbus succeeded"; } else kDebug() << "couldn't get ksmserver interface"; } /*! Returns a SessionInfo for client \a c. The returned session info is removed from the storage. It's up to the caller to delete it. This function is called when a new window is mapped and must be managed. We try to find a matching entry in the session. May return 0 if there's no session info for the client. */ SessionInfo* Workspace::takeSessionInfo( Client* c ) { SessionInfo *realInfo = 0; QByteArray sessionId = c->sessionId(); QByteArray windowRole = c->windowRole(); QByteArray wmCommand = c->wmCommand(); QByteArray wmClientMachine = c->wmClientMachine( true ); QByteArray resourceName = c->resourceName(); QByteArray resourceClass = c->resourceClass(); // First search ``session'' if (! sessionId.isEmpty() ) { // look for a real session managed client (algorithm suggested by ICCCM) foreach( SessionInfo* info, session ) { if( realInfo ) break; if( info->sessionId == sessionId && sessionInfoWindowTypeMatch( c, info )) { if( ! windowRole.isEmpty() ) { if( info->windowRole == windowRole ) { realInfo = info; session.removeAll(info); } } else { if( info->windowRole.isEmpty() && info->resourceName == resourceName && info->resourceClass == resourceClass ) { realInfo = info; session.removeAll(info); } } } } } else { // look for a sessioninfo with matching features. foreach( SessionInfo* info, session ) { if( realInfo ) break; if( info->resourceName == resourceName && info->resourceClass == resourceClass && info->wmClientMachine == wmClientMachine && sessionInfoWindowTypeMatch( c, info )) { if ( wmCommand.isEmpty() || info->wmCommand == wmCommand ) { realInfo = info; session.removeAll( info ); } } } } // Set clientGroupClient for other clients in the same group if( realInfo && realInfo->clientGroup ) foreach( SessionInfo* info, session ) if( !info->clientGroupClient && info->clientGroup == realInfo->clientGroup ) info->clientGroupClient = c; return realInfo; } bool Workspace::sessionInfoWindowTypeMatch( Client* c, SessionInfo* info ) { if( info->windowType == -2 ) { // undefined (not really part of NET::WindowType) return !c->isSpecialWindow(); } return info->windowType == c->windowType(); } static const char* const window_type_names[] = { "Unknown", "Normal" , "Desktop", "Dock", "Toolbar", "Menu", "Dialog", "Override", "TopMenu", "Utility", "Splash" }; // change also the two functions below when adding new entries const char* Workspace::windowTypeToTxt( NET::WindowType type ) { if( type >= NET::Unknown && type <= NET::Splash ) return window_type_names[ type + 1 ]; // +1 (unknown==-1) if( type == -2 ) // undefined (not really part of NET::WindowType) return "Undefined"; kFatal(1212) << "Unknown Window Type" ; return NULL; } NET::WindowType Workspace::txtToWindowType( const char* txt ) { for( int i = NET::Unknown; i <= NET::Splash; ++i ) if( qstrcmp( txt, window_type_names[ i + 1 ] ) == 0 ) // +1 return static_cast< NET::WindowType >( i ); return static_cast< NET::WindowType >( -2 ); // undefined } // KWin's focus stealing prevention causes problems with user interaction // during session save, as it prevents possible dialogs from getting focus. // Therefore it's temporarily disabled during session saving. Start of // session saving can be detected in SessionManager::saveState() above, // but Qt doesn't have API for saying when session saved finished (either // successfully, or was canceled). Therefore, create another connection // to session manager, that will provide this information. // Similarly the remember feature of window-specific settings should be disabled // during KDE shutdown when windows may move e.g. because of Kicker going away // (struts changing). When session saving starts, it can be cancelled, in which // case the shutdown_cancelled callback is invoked, or it's a checkpoint that // is immediatelly followed by save_complete, or finally it's a shutdown that // is immediatelly followed by die callback. So getting save_yourself with shutdown // set disables window-specific settings remembering, getting shutdown_cancelled // re-enables, otherwise KWin will go away after die. static void save_yourself( SmcConn conn_P, SmPointer ptr, int, Bool shutdown, int, Bool ) { SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr ); if( conn_P != session->connection()) return; if( shutdown ) Workspace::self()->disableRulesUpdates( true ); SmcSaveYourselfDone( conn_P, True ); } static void die( SmcConn conn_P, SmPointer ptr ) { SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr ); if( conn_P != session->connection()) return; // session->saveDone(); we will quit anyway session->close(); } static void save_complete( SmcConn conn_P, SmPointer ptr ) { SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr ); if( conn_P != session->connection()) return; session->saveDone(); } static void shutdown_cancelled( SmcConn conn_P, SmPointer ptr ) { SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr ); if( conn_P != session->connection()) return; Workspace::self()->disableRulesUpdates( false ); // re-enable // no need to differentiate between successful finish and cancel session->saveDone(); } void SessionSaveDoneHelper::saveDone() { Workspace::self()->sessionSaveDone(); } SessionSaveDoneHelper::SessionSaveDoneHelper() { SmcCallbacks calls; calls.save_yourself.callback = save_yourself; calls.save_yourself.client_data = reinterpret_cast< SmPointer >(this); calls.die.callback = die; calls.die.client_data = reinterpret_cast< SmPointer >(this); calls.save_complete.callback = save_complete; calls.save_complete.client_data = reinterpret_cast< SmPointer >(this); calls.shutdown_cancelled.callback = shutdown_cancelled; calls.shutdown_cancelled.client_data = reinterpret_cast< SmPointer >(this); char* id = NULL; char err[ 11 ]; conn = SmcOpenConnection( NULL, 0, 1, 0, SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask, &calls, NULL, &id, 10, err ); if( id != NULL ) free( id ); if( conn == NULL ) return; // no SM // set the required properties, mostly dummy values SmPropValue propvalue[ 5 ]; SmProp props[ 5 ]; propvalue[ 0 ].length = sizeof( unsigned char); unsigned char value0 = SmRestartNever; // so that this extra SM connection doesn't interfere propvalue[ 0 ].value = &value0; props[ 0 ].name = const_cast< char* >( SmRestartStyleHint ); props[ 0 ].type = const_cast< char* >( SmCARD8 ); props[ 0 ].num_vals = 1; props[ 0 ].vals = &propvalue[ 0 ]; struct passwd* entry = getpwuid( geteuid() ); propvalue[ 1 ].length = entry != NULL ? strlen( entry->pw_name ) : 0; propvalue[ 1 ].value = (SmPointer)( entry != NULL ? entry->pw_name : "" ); props[ 1 ].name = const_cast< char* >( SmUserID ); props[ 1 ].type = const_cast< char* >( SmARRAY8 ); props[ 1 ].num_vals = 1; props[ 1 ].vals = &propvalue[ 1 ]; propvalue[ 2 ].length = 0; propvalue[ 2 ].value = (SmPointer)( "" ); props[ 2 ].name = const_cast< char* >( SmRestartCommand ); props[ 2 ].type = const_cast< char* >( SmLISTofARRAY8 ); props[ 2 ].num_vals = 1; props[ 2 ].vals = &propvalue[ 2 ]; propvalue[ 3 ].length = strlen( "kwinsmhelper" ); propvalue[ 3 ].value = (SmPointer)"kwinsmhelper"; props[ 3 ].name = const_cast< char* >( SmProgram ); props[ 3 ].type = const_cast< char* >( SmARRAY8 ); props[ 3 ].num_vals = 1; props[ 3 ].vals = &propvalue[ 3 ]; propvalue[ 4 ].length = 0; propvalue[ 4 ].value = (SmPointer)( "" ); props[ 4 ].name = const_cast< char* >( SmCloneCommand ); props[ 4 ].type = const_cast< char* >( SmLISTofARRAY8 ); props[ 4 ].num_vals = 1; props[ 4 ].vals = &propvalue[ 4 ]; SmProp* p[ 5 ] = { &props[ 0 ], &props[ 1 ], &props[ 2 ], &props[ 3 ], &props[ 4 ] }; SmcSetProperties( conn, 5, p ); notifier = new QSocketNotifier( IceConnectionNumber( SmcGetIceConnection( conn )), QSocketNotifier::Read, this ); connect( notifier, SIGNAL( activated( int )), SLOT( processData())); } SessionSaveDoneHelper::~SessionSaveDoneHelper() { close(); } void SessionSaveDoneHelper::close() { if( conn != NULL ) { delete notifier; SmcCloseConnection( conn, 0, NULL ); } conn = NULL; } void SessionSaveDoneHelper::processData() { if( conn != NULL ) IceProcessMessages( SmcGetIceConnection( conn ), 0, 0 ); } +void Workspace::sessionSaveDone() +{ + session_saving = false; + //remove sessionInteract flag from all clients + foreach( Client* c, clients ) { + c->setSessionInteract(false); + } +} + } // namespace #include "sm.moc" diff --git a/workspace.h b/workspace.h index 12ec25920..0eec15df3 100644 --- a/workspace.h +++ b/workspace.h @@ -1,1421 +1,1416 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak Copyright (C) 2009 Lucas Murray This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_WORKSPACE_H #define KWIN_WORKSPACE_H #include #include #include #include #include #include #include #include #include "kactivitycontroller.h" #include "plugins.h" #include "utils.h" #include "kdecoration.h" #include "kdecorationfactory.h" #include "sm.h" #include // TODO: Cleanup the order of things in this .h file class QMenu; class QActionGroup; class QStringList; class KConfig; class KActionCollection; class KStartupInfo; class KStartupInfoId; class KStartupInfoData; class QSlider; class QPushButton; namespace KWin { namespace TabBox { class TabBox; } class Client; class Tile; class TilingLayout; class ClientGroup; class DesktopChangeOSD; class RootInfo; class PluginMgr; class Placement; class Rules; class WindowRules; class Workspace : public QObject, public KDecorationDefines { Q_OBJECT public: Workspace( bool restore = false ); virtual ~Workspace(); static Workspace* self() { return _self; } bool workspaceEvent( XEvent* ); bool workspaceEvent( QEvent* ); KDecoration* createDecoration( KDecorationBridge* bridge ); bool hasClient( const Client* ); template Client* findClient( T predicate ) const; template void forEachClient( T1 procedure, T2 predicate ); template void forEachClient( T procedure ); template Unmanaged* findUnmanaged( T predicate ) const; template void forEachUnmanaged( T1 procedure, T2 predicate ); template void forEachUnmanaged( T procedure ); QRect clientArea( clientAreaOption, const QPoint& p, int desktop ) const; QRect clientArea( clientAreaOption, const Client* c ) const; QRect clientArea( clientAreaOption, int screen, int desktop ) const; QRegion restrictedMoveArea( int desktop, StrutAreas areas = StrutAreaAll ) const; QRegion previousRestrictedMoveArea( int desktop, StrutAreas areas = StrutAreaAll ) const; /** * @internal */ void killWindowId( Window window); void killWindow() { slotKillWindow(); } bool initializing() const; /** * Returns the active client, i.e. the client that has the focus (or None * if no client has the focus) */ Client* activeClient() const; /** * Client that was activated, but it's not yet really activeClient(), because * we didn't process yet the matching FocusIn event. Used mostly in focus * stealing prevention code. */ Client* mostRecentlyActivatedClient() const; void activateClient( Client*, bool force = false ); void requestFocus( Client* c, bool force = false ); void takeActivity( Client* c, int flags, bool handled ); // Flags are ActivityFlags void handleTakeActivity( Client* c, Time timestamp, int flags ); // Flags are ActivityFlags bool allowClientActivation( const Client* c, Time time = -1U, bool focus_in = false, bool ignore_desktop = false ); void restoreFocus(); void gotFocusIn( const Client* ); void setShouldGetFocus( Client* ); bool fakeRequestedActivity( Client* c ); void unfakeActivity( Client* c ); bool activateNextClient( Client* c ); bool focusChangeEnabled() { return block_focus == 0; } void updateColormap(); /** * Indicates that the client c is being moved around by the user. */ void setClientIsMoving( Client* c ); void place( Client* c, QRect& area ); void placeSmart( Client* c, const QRect& area ); QPoint adjustClientPosition( Client* c, QPoint pos, bool unrestricted, double snapAdjust = 1.0 ); QRect adjustClientSize( Client* c, QRect moveResizeGeom, int mode ); void raiseClient( Client* c, bool nogroup = false ); void lowerClient( Client* c, bool nogroup = false ); void raiseClientRequest( Client* c, NET::RequestSource src, Time timestamp ); void lowerClientRequest( Client* c, NET::RequestSource src, Time timestamp ); void restackClientUnderActive( Client* ); void restack( Client *c, Client *under ); void updateClientLayer( Client* c ); void raiseOrLowerClient( Client* ); void restoreSessionStackingOrder( Client* c ); void updateStackingOrder( bool propagate_new_clients = false ); void forceRestacking(); void clientHidden( Client* ); void clientAttentionChanged( Client* c, bool set ); void checkElectricBorder(const QPoint& pos, Time time); void restoreElectricBorderSize( ElectricBorder border ); void reserveElectricBorder( ElectricBorder border ); void unreserveElectricBorder( ElectricBorder border ); void reserveElectricBorderActions( bool reserve ); void reserveElectricBorderSwitching( bool reserve ); //------------------------------------------------- // Tiling public: bool tilingEnabled() const; void setTilingEnabled( bool tiling ); bool tileable( Client *c ); void createTile( Client *c ); // updates geometry of tiles on all desktops, // this rearranges the tiles. void updateAllTiles(); // The notification functions are called from // various points in existing code so that // tiling can take any action if required. // They are defined in tiling.cpp void notifyTilingWindowResize( Client *c, const QRect &moveResizeGeom, const QRect &orig ); void notifyTilingWindowMove( Client *c, const QRect &moveResizeGeom, const QRect &orig ); void notifyTilingWindowResizeDone( Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled ); void notifyTilingWindowMoveDone( Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled ); void notifyTilingWindowDesktopChanged( Client *c, int old_desktop ); void notifyTilingWindowActivated( Client *c ); void notifyTilingWindowMinimizeToggled( Client *c ); void notifyTilingWindowMaximized( Client *c, WindowOperation op ); Position supportedTilingResizeMode( Client *c, Position currentMode ); //------------------------------------------------- // Desktop layout public: /** * @returns Total number of desktops currently in existence. */ int numberOfDesktops() const; /** * Set the number of available desktops to @a count. This function overrides any previous * grid layout. */ void setNumberOfDesktops( int count ); /** * Called from within setNumberOfDesktops() to ensure the desktop layout is still valid. */ void updateDesktopLayout(); /** * @returns The size of desktop layout in grid units. */ QSize desktopGridSize() const; /** * @returns The width of desktop layout in grid units. */ int desktopGridWidth() const; /** * @returns The height of desktop layout in grid units. */ int desktopGridHeight() const; /** * @returns The width of desktop layout in pixels. Equivalent to gridWidth() * * ::displayWidth(). */ int workspaceWidth() const; /** * @returns The height of desktop layout in pixels. Equivalent to gridHeight() * * ::displayHeight(). */ int workspaceHeight() const; /** * @returns The ID of the current desktop. */ int currentDesktop() const; /** * Set the current desktop to @a current. * @returns True on success, false otherwise. */ bool setCurrentDesktop( int current ); /** * Generate a desktop layout from EWMH _NET_DESKTOP_LAYOUT property parameters. */ void setNETDesktopLayout( Qt::Orientation orientation, int width, int height, int startingCorner ); /** * @returns The ID of the desktop at the point @a coords or 0 if no desktop exists at that * point. @a coords is to be in grid units. */ int desktopAtCoords( QPoint coords ) const; /** * @returns The coords of desktop @a id in grid units. */ QPoint desktopGridCoords( int id ) const; /** * @returns The coords of the top-left corner of desktop @a id in pixels. */ QPoint desktopCoords( int id ) const; /** * @returns The ID of the desktop above desktop @a id. Wraps around to the bottom of * the layout if @a wrap is set. If @a id is not set use the current one. */ int desktopAbove( int id = 0, bool wrap = true ) const; /** * @returns The ID of the desktop to the right of desktop @a id. Wraps around to the * left of the layout if @a wrap is set. If @a id is not set use the current one. */ int desktopToRight( int id = 0, bool wrap = true ) const; /** * @returns The ID of the desktop below desktop @a id. Wraps around to the top of the * layout if @a wrap is set. If @a id is not set use the current one. */ int desktopBelow( int id = 0, bool wrap = true ) const; /** * @returns The ID of the desktop to the left of desktop @a id. Wraps around to the * right of the layout if @a wrap is set. If @a id is not set use the current one. */ int desktopToLeft( int id = 0, bool wrap = true ) const; /** * @returns Whether or not the layout is allowed to be modified by the user. */ bool isDesktopLayoutDynamic() const; /** * Sets whether or not this layout can be modified by the user. */ void setDesktopLayoutDynamicity( bool dynamicity ); /** * Create new desktop at the point @a coords * @returns The ID of the created desktop */ int addDesktop( QPoint coords ); /** * Deletes the desktop with the ID @a id. All desktops with an ID greater than the one that * was deleted will have their IDs' decremented. */ void deleteDesktop( int id ); private: int desktopCount_; QSize desktopGridSize_; int* desktopGrid_; int currentDesktop_; QString activity_; QStringList allActivities_; bool desktopLayoutDynamicity_; KActivityController activityController_; bool tilingEnabled_; // Each tilingLayout is for one virtual desktop. // The length is always one more than the number of // virtual desktops so that we can quickly index them // without having to remember to subtract one. QVector tilingLayouts; //------------------------------------------------- // Unsorted public: int activeScreen() const; int numScreens() const; void checkActiveScreen( const Client* c ); void setActiveScreenMouse( const QPoint& mousepos ); QRect screenGeometry( int screen ) const; int screenNumber( const QPoint& pos ) const; QString currentActivity() const { return activity_; } QStringList activityList() const { return allActivities_; } QStringList openActivityList() const { return activityController_.listActivities(KActivityInfo::Running); } // Tab box Client* currentTabBoxClient() const; ClientList currentTabBoxClientList() const; int currentTabBoxDesktop() const; QList currentTabBoxDesktopList() const; void setTabBoxClient( Client* ); void setTabBoxDesktop( int ); Client* nextClientFocusChain( Client* ) const; Client* previousClientFocusChain( Client* ) const; Client* nextClientStatic( Client* ) const; Client* previousClientStatic( Client* ) const; int nextDesktopFocusChain( int iDesktop ) const; int previousDesktopFocusChain( int iDesktop ) const; int nextDesktopStatic( int iDesktop ) const; int previousDesktopStatic( int iDesktop ) const; void refTabBox(); void unrefTabBox(); void closeTabBox( bool abort = false ); // Tabbing void addClientGroup( ClientGroup* group ); void removeClientGroup( ClientGroup* group ); /// Returns the index of c in clientGroupList. int indexOfClientGroup( ClientGroup* group ); /// Change the client c_id to the group with index g_id void moveItemToClientGroup( ClientGroup* oldGroup, int oldIndex, ClientGroup* group, int index = -1 ); Client* findSimilarClient( Client* c ); QList clientGroups; // List of existing clients groups with no special order /** * Returns the list of clients sorted in stacking order, with topmost client * at the last position */ const ClientList& stackingOrder() const; ToplevelList xStackingOrder() const; ClientList ensureStackingOrder( const ClientList& clients ) const; Client* topClientOnDesktop( int desktop, int screen, bool unconstrained = false, bool only_normal = true ) const; Client* findDesktop( bool topmost, int desktop ) const; void sendClientToDesktop( Client* c, int desktop, bool dont_activate ); void toggleClientOnActivity( Client* c, const QString &activity, bool dont_activate ); void windowToPreviousDesktop( Client* c ); void windowToNextDesktop( Client* c ); void sendClientToScreen( Client* c, int screen ); // KDE4 remove me - And it's also in the DCOP interface :( void showWindowMenuAt( unsigned long id, int x, int y ); void toggleCompositing(); void loadEffect( const QString& name ); void toggleEffect( const QString& name ); void reconfigureEffect( const QString& name ); void unloadEffect( const QString& name ); QStringList loadedEffects() const; QStringList listOfEffects() const; /** * Shows the menu operations menu for the client and makes it active if * it's not already. */ void showWindowMenu( const QRect& pos, Client* cl ); /** * Backwards compatibility. */ void showWindowMenu( int x, int y, Client* cl ); void showWindowMenu( QPoint pos, Client* cl ); void updateMinimizedOfTransients( Client* ); void updateOnAllDesktopsOfTransients( Client* ); void updateOnAllActivitiesOfTransients( Client* ); void checkTransients( Window w ); void performWindowOperation( Client* c, WindowOperation op ); void storeSession( KConfig* config, SMSavePhase phase ); void storeClient( KConfigGroup &cg, int num, Client *c ); void storeSubSession( const QString &name, QSet sessionIds ); SessionInfo* takeSessionInfo( Client* ); WindowRules findWindowRules( const Client*, bool ); void rulesUpdated(); void discardUsedWindowRules( Client* c, bool withdraw ); void disableRulesUpdates( bool disable ); bool rulesUpdatesDisabled() const; bool hasDecorationShadows() const; bool decorationHasAlpha() const; bool decorationSupportsClientGrouping() const; // Returns true if the decoration supports tabs. bool decorationSupportsFrameOverlap() const; bool decorationSupportsBlurBehind() const; // D-Bus interface void cascadeDesktop(); void unclutterDesktop(); void doNotManage( const QString& ); QList decorationSupportedColors() const; void nextDesktop(); void previousDesktop(); void circulateDesktopApplications(); bool compositingActive(); bool waitForCompositingSetup(); void toggleTiling(); void nextTileLayout(); void previousTileLayout(); void stopActivity( const QString &id ); void startActivity( const QString &id ); void setCurrentScreen( int new_screen ); QString desktopName( int desk ) const; void setShowingDesktop( bool showing ); void resetShowingDesktop( bool keep_hidden ); bool showingDesktop() const; bool isNotManaged( const QString& title ); // TODO: Setter or getter? void sendPingToWindow( Window w, Time timestamp ); // Called from Client::pingWindow() void sendTakeActivity( Client* c, Time timestamp, long flags ); // Called from Client::takeActivity() void removeClient( Client*, allowed_t ); // Only called from Client::destroyClient() or Client::releaseWindow() void setActiveClient( Client*, allowed_t ); Group* findGroup( Window leader ) const; void addGroup( Group* group, allowed_t ); void removeGroup( Group* group, allowed_t ); Group* findClientLeaderGroup( const Client* c ) const; void removeUnmanaged( Unmanaged*, allowed_t ); // Only called from Unmanaged::release() void removeDeleted( Deleted*, allowed_t ); void addDeleted( Deleted*, allowed_t ); bool checkStartupNotification( Window w, KStartupInfoId& id, KStartupInfoData& data ); void focusToNull(); // SELI TODO: Public? enum FocusChainChange { FocusChainMakeFirst, FocusChainMakeLast, FocusChainUpdate }; void updateFocusChains( Client* c, FocusChainChange change ); bool forcedGlobalMouseGrab() const; void clientShortcutUpdated( Client* c ); bool shortcutAvailable( const KShortcut& cut, Client* ignore = NULL ) const; bool globalShortcutsDisabled() const; void disableGlobalShortcuts( bool disable ); void disableGlobalShortcutsForClient( bool disable ); QPoint cursorPos() const; void sessionSaveStarted(); void sessionSaveDone(); void setWasUserInteraction(); bool wasUserInteraction() const; bool sessionSaving() const; bool managingTopMenus() const; int topMenuHeight() const; void updateCurrentTopMenu(); int packPositionLeft( const Client* cl, int oldx, bool left_edge ) const; int packPositionRight( const Client* cl, int oldx, bool right_edge ) const; int packPositionUp( const Client* cl, int oldy, bool top_edge ) const; int packPositionDown( const Client* cl, int oldy, bool bottom_edge ) const; static QStringList configModules(bool controlCenter); void cancelDelayFocus(); void requestDelayFocus( Client* ); void updateFocusMousePosition( const QPoint& pos ); QPoint focusMousePosition() const; void toggleTopDockShadows(bool on); // when adding repaints caused by a window, you probably want to use // either Toplevel::addRepaint() or Toplevel::addWorkspaceRepaint() void addRepaint( const QRect& r ); void addRepaint( const QRegion& r ); void addRepaint( int x, int y, int w, int h ); /// Creates XComposite overlay window, call initOverlay() afterwards bool createOverlay(); /// Init overlay and the destination window in it void setupOverlay( Window window ); void showOverlay(); void hideOverlay(); // hides and resets overlay window void setOverlayShape( const QRegion& reg ); /// Destroys XComposite overlay window void destroyOverlay(); Window overlayWindow(); void checkUnredirect( bool force = false ); void checkCompositeTimer(); // Mouse polling void startMousePolling(); void stopMousePolling(); void raiseElectricBorderWindows(); void showElectricBorderWindowOutline(); void hideElectricBorderWindowOutline(); public slots: void addRepaintFull(); void refresh(); // Keybindings void slotSwitchDesktopNext(); void slotSwitchDesktopPrevious(); void slotSwitchDesktopRight(); void slotSwitchDesktopLeft(); void slotSwitchDesktopUp(); void slotSwitchDesktopDown(); void slotSwitchToDesktop( int ); void slotSwitchToDesktop1() { return slotSwitchToDesktop( 1 ); } void slotSwitchToDesktop2() { return slotSwitchToDesktop( 2 ); } void slotSwitchToDesktop3() { return slotSwitchToDesktop( 3 ); } void slotSwitchToDesktop4() { return slotSwitchToDesktop( 4 ); } void slotSwitchToDesktop5() { return slotSwitchToDesktop( 5 ); } void slotSwitchToDesktop6() { return slotSwitchToDesktop( 6 ); } void slotSwitchToDesktop7() { return slotSwitchToDesktop( 7 ); } void slotSwitchToDesktop8() { return slotSwitchToDesktop( 8 ); } void slotSwitchToDesktop9() { return slotSwitchToDesktop( 9 ); } void slotSwitchToDesktop10() { return slotSwitchToDesktop( 10 ); } void slotSwitchToDesktop11() { return slotSwitchToDesktop( 11 ); } void slotSwitchToDesktop12() { return slotSwitchToDesktop( 12 ); } void slotSwitchToDesktop13() { return slotSwitchToDesktop( 13 ); } void slotSwitchToDesktop14() { return slotSwitchToDesktop( 14 ); } void slotSwitchToDesktop15() { return slotSwitchToDesktop( 15 ); } void slotSwitchToDesktop16() { return slotSwitchToDesktop( 16 ); } void slotSwitchToDesktop17() { return slotSwitchToDesktop( 17 ); } void slotSwitchToDesktop18() { return slotSwitchToDesktop( 18 ); } void slotSwitchToDesktop19() { return slotSwitchToDesktop( 19 ); } void slotSwitchToDesktop20() { return slotSwitchToDesktop( 20 ); } //void slotSwitchToWindow( int ); void slotWindowToDesktop( int ); void slotWindowToDesktop1() { return slotWindowToDesktop( 1 ); } void slotWindowToDesktop2() { return slotWindowToDesktop( 2 ); } void slotWindowToDesktop3() { return slotWindowToDesktop( 3 ); } void slotWindowToDesktop4() { return slotWindowToDesktop( 4 ); } void slotWindowToDesktop5() { return slotWindowToDesktop( 5 ); } void slotWindowToDesktop6() { return slotWindowToDesktop( 6 ); } void slotWindowToDesktop7() { return slotWindowToDesktop( 7 ); } void slotWindowToDesktop8() { return slotWindowToDesktop( 8 ); } void slotWindowToDesktop9() { return slotWindowToDesktop( 9 ); } void slotWindowToDesktop10() { return slotWindowToDesktop( 10 ); } void slotWindowToDesktop11() { return slotWindowToDesktop( 11 ); } void slotWindowToDesktop12() { return slotWindowToDesktop( 12 ); } void slotWindowToDesktop13() { return slotWindowToDesktop( 13 ); } void slotWindowToDesktop14() { return slotWindowToDesktop( 14 ); } void slotWindowToDesktop15() { return slotWindowToDesktop( 15 ); } void slotWindowToDesktop16() { return slotWindowToDesktop( 16 ); } void slotWindowToDesktop17() { return slotWindowToDesktop( 17 ); } void slotWindowToDesktop18() { return slotWindowToDesktop( 18 ); } void slotWindowToDesktop19() { return slotWindowToDesktop( 19 ); } void slotWindowToDesktop20() { return slotWindowToDesktop( 20 ); } //void slotWindowToListPosition( int ); void slotSwitchToScreen( int ); void slotSwitchToScreen0() { return slotSwitchToScreen( 0 ); } void slotSwitchToScreen1() { return slotSwitchToScreen( 1 ); } void slotSwitchToScreen2() { return slotSwitchToScreen( 2 ); } void slotSwitchToScreen3() { return slotSwitchToScreen( 3 ); } void slotSwitchToScreen4() { return slotSwitchToScreen( 4 ); } void slotSwitchToScreen5() { return slotSwitchToScreen( 5 ); } void slotSwitchToScreen6() { return slotSwitchToScreen( 6 ); } void slotSwitchToScreen7() { return slotSwitchToScreen( 7 ); } void slotWindowToScreen( int ); void slotWindowToScreen0() { return slotWindowToScreen( 0 ); } void slotWindowToScreen1() { return slotWindowToScreen( 1 ); } void slotWindowToScreen2() { return slotWindowToScreen( 2 ); } void slotWindowToScreen3() { return slotWindowToScreen( 3 ); } void slotWindowToScreen4() { return slotWindowToScreen( 4 ); } void slotWindowToScreen5() { return slotWindowToScreen( 5 ); } void slotWindowToScreen6() { return slotWindowToScreen( 6 ); } void slotWindowToScreen7() { return slotWindowToScreen( 7 ); } void slotSwitchToNextScreen(); void slotWindowToNextScreen(); void slotToggleShowDesktop(); void slotWindowMaximize(); void slotWindowMaximizeVertical(); void slotWindowMaximizeHorizontal(); void slotWindowMinimize(); void slotWindowShade(); void slotWindowRaise(); void slotWindowLower(); void slotWindowRaiseOrLower(); void slotActivateAttentionWindow(); void slotWindowPackLeft(); void slotWindowPackRight(); void slotWindowPackUp(); void slotWindowPackDown(); void slotWindowGrowHorizontal(); void slotWindowGrowVertical(); void slotWindowShrinkHorizontal(); void slotWindowShrinkVertical(); void slotWindowQuickTileLeft(); void slotWindowQuickTileRight(); void slotWalkThroughDesktops(); void slotWalkBackThroughDesktops(); void slotWalkThroughDesktopList(); void slotWalkBackThroughDesktopList(); void slotWalkThroughWindows(); void slotWalkBackThroughWindows(); void slotWalkThroughWindowsAlternative(); void slotWalkBackThroughWindowsAlternative(); void slotWalkThroughDesktopsKeyChanged( const QKeySequence& seq ); void slotWalkBackThroughDesktopsKeyChanged( const QKeySequence& seq ); void slotWalkThroughDesktopListKeyChanged( const QKeySequence& seq ); void slotWalkBackThroughDesktopListKeyChanged( const QKeySequence& seq ); void slotWalkThroughWindowsKeyChanged( const QKeySequence& seq ); void slotWalkBackThroughWindowsKeyChanged( const QKeySequence& seq ); void slotMoveToTabLeftKeyChanged( const QKeySequence& seq ); void slotMoveToTabRightKeyChanged( const QKeySequence& seq ); void slotWalkThroughWindowsAlternativeKeyChanged( const QKeySequence& seq ); void slotWalkBackThroughWindowsAlternativeKeyChanged( const QKeySequence& seq ); void slotSwitchWindowUp(); void slotSwitchWindowDown(); void slotSwitchWindowRight(); void slotSwitchWindowLeft(); void slotWindowOperations(); void slotWindowClose(); void slotWindowMove(); void slotWindowResize(); void slotWindowAbove(); void slotWindowBelow(); void slotWindowOnAllDesktops(); void slotWindowFullScreen(); void slotWindowNoBorder(); void slotWindowToNextDesktop(); void slotWindowToPreviousDesktop(); void slotWindowToDesktopRight(); void slotWindowToDesktopLeft(); void slotWindowToDesktopUp(); void slotWindowToDesktopDown(); void slotMouseEmulation(); void slotDisableGlobalShortcuts(); void slotSettingsChanged( int category ); void reconfigure(); void slotReconfigure(); void slotReinitCompositing(); void resetCompositing(); void slotKillWindow(); void slotGrabWindow(); void slotGrabDesktop(); void slotSetupWindowShortcut(); void setupWindowShortcutDone( bool ); void slotToggleCompositing(); void updateClientArea(); void suspendCompositing(); void suspendCompositing( bool suspend ); // user actions, usually bound to shortcuts // and also provided through the D-BUS interface. void slotToggleTiling(); void slotToggleFloating(); void slotNextTileLayout(); void slotPreviousTileLayout(); // Changes the focused client void slotFocusTileLeft(); void slotFocusTileRight(); void slotFocusTileTop(); void slotFocusTileBottom(); // swaps active and adjacent client. void slotMoveTileLeft(); void slotMoveTileRight(); void slotMoveTileTop(); void slotMoveTileBottom(); void belowCursor(); // NOTE: debug method void dumpTiles() const; void slotSwitchToTabLeft(); // Slot to move left the active Client. void slotSwitchToTabRight(); // Slot to move right the active Client. void slotRemoveFromGroup(); // Slot to remove the active client from its group. private slots: void groupTabPopupAboutToShow(); // Popup to add to another group void switchToTabPopupAboutToShow(); // Popup to move in the group void slotAddToTabGroup( QAction* ); // Add client to a group void slotSwitchToTab( QAction* ); // Change the tab void desktopPopupAboutToShow(); void activityPopupAboutToShow(); void clientPopupAboutToShow(); void slotSendToDesktop( QAction* ); void slotToggleOnActivity( QAction* ); void clientPopupActivated( QAction* ); void configureWM(); void desktopResized(); void slotUpdateToolWindows(); void lostTopMenuSelection(); void lostTopMenuOwner(); void delayFocus(); void gotTemporaryRulesMessage( const QString& ); void cleanupTemporaryRules(); void writeWindowRules(); void slotBlockShortcuts(int data); void slotReloadConfig(); void setPopupClientOpacity( QAction* action ); void setupCompositing(); void finishCompositing(); void fallbackToXRenderCompositing(); void performCompositing(); void performMousePoll(); void lostCMSelection(); void updateElectricBorders(); void resetCursorPosTime(); void delayedCheckUnredirect(); void updateCurrentActivity(const QString &new_activity); void activityRemoved(const QString &activity); void activityAdded(const QString &activity); void reallyStopActivity( const QString &id ); //dbus deadlocks suck protected: bool keyPressMouseEmulation( XKeyEvent& ev ); Q_SIGNALS: Q_SCRIPTABLE void compositingToggled( bool active ); //Signals required for the scripting interface signals: void desktopPresenceChanged(KWin::Client*, int); void currentDesktopChanged(int); void clientAdded(KWin::Client*); void clientRemoved(KWin::Client*); void clientActivated(KWin::Client*); void groupAdded(KWin::Group*); private: void init(); void initShortcuts(); void readShortcuts(); void initDesktopPopup(); void initActivityPopup(); void discardPopup(); void setupWindowShortcut( Client* c ); void checkCursorPos(); enum Direction { DirectionNorth, DirectionEast, DirectionSouth, DirectionWest }; void switchWindow( Direction direction ); bool startKDEWalkThroughWindows( TabBoxMode mode ); // TabBoxWindowsMode | TabBoxWindowsAlternativeMode bool startWalkThroughDesktops( TabBoxMode mode ); // TabBoxDesktopMode | TabBoxDesktopListMode bool startWalkThroughDesktops(); bool startWalkThroughDesktopList(); void navigatingThroughWindows( bool forward, const KShortcut& shortcut, TabBoxMode mode ); // TabBoxWindowsMode | TabBoxWindowsAlternativeMode void KDEWalkThroughWindows( bool forward ); void CDEWalkThroughWindows( bool forward ); void walkThroughDesktops( bool forward ); void KDEOneStepThroughWindows( bool forward, TabBoxMode mode ); // TabBoxWindowsMode | TabBoxWindowsAlternativeMode void oneStepThroughDesktops( bool forward, TabBoxMode mode ); // TabBoxDesktopMode | TabBoxDesktopListMode void oneStepThroughDesktops( bool forward ); void oneStepThroughDesktopList( bool forward ); bool establishTabBoxGrab(); void removeTabBoxGrab(); void propagateClients( bool propagate_new_clients ); // Called only from updateStackingOrder ClientList constrainedStackingOrder(); void raiseClientWithinApplication( Client* c ); void lowerClientWithinApplication( Client* c ); bool allowFullClientRaising( const Client* c, Time timestamp ); bool keepTransientAbove( const Client* mainwindow, const Client* transient ); void blockStackingUpdates( bool block ); void addTopMenu( Client* c ); void removeTopMenu( Client* c ); void setupTopMenuHandling(); void updateTopMenuGeometry( Client* c = NULL ); void updateToolWindows( bool also_hide ); void fixPositionAfterCrash( Window w, const XWindowAttributes& attr ); /// This is the right way to create a new client Client* createClient( Window w, bool is_mapped ); void addClient( Client* c, allowed_t ); Unmanaged* createUnmanaged( Window w ); void addUnmanaged( Unmanaged* c, allowed_t ); Window findSpecialEventWindow( XEvent* e ); void randomPlacement( Client* c ); void smartPlacement( Client* c ); void cascadePlacement( Client* c, bool re_init = false ); // Desktop names and number of desktops void loadDesktopSettings(); void saveDesktopSettings(); // Mouse emulation WId getMouseEmulationWindow(); enum MouseEmulation { EmuPress, EmuRelease, EmuMove }; unsigned int sendFakedMouseEvent( const QPoint& pos, WId win, MouseEmulation type, int button, unsigned int state ); // returns the new state void tabBoxKeyPress( int key ); void tabBoxKeyRelease( const XKeyEvent& ev ); // Electric borders void destroyElectricBorders(); bool electricBorderEvent(XEvent * e); void electricBorderSwitchDesktop( ElectricBorder border, const QPoint& pos ); //--------------------------------------------------------------------- void helperDialog( const QString& message, const Client* c ); QMenu* clientPopup(); void closeActivePopup(); void updateClientArea( bool force ); bool windowRepaintsPending() const; void setCompositeTimer(); void checkCompositePaintTime( int msec ); QVector desktop_focus_chain; QWidget* active_popup; Client* active_popup_client; void loadSessionInfo(); void addSessionInfo( KConfigGroup &cg ); void loadSubSessionInfo( const QString &name ); void loadWindowRules(); void editWindowRules( Client* c, bool whole_app ); QList session; QList rules; KXMessages temporaryRulesMessages; QTimer rulesUpdatedTimer; bool rules_updates_disabled; static const char* windowTypeToTxt( NET::WindowType type ); static NET::WindowType txtToWindowType( const char* txt ); static bool sessionInfoWindowTypeMatch( Client* c, SessionInfo* info ); // try to get a decent tile, either the one with // focus or the one below the mouse. Tile* getNiceTile() const; void removeTile( Client *c ); // int, and not Tile::Direction because // we are using a forward declaration for Tile Tile* findAdjacentTile( Tile *ref, int d ); void focusTile( int d ); void moveTile( int d ); Client* active_client; Client* last_active_client; Client* most_recently_raised; // Used ONLY by raiseOrLowerClient() Client* movingClient; Client* pending_take_activity; int active_screen; // Delay(ed) window focus timer and client QTimer* delayFocusTimer; Client* delayfocus_client; QPoint focusMousePos; ClientList clients; ClientList desktops; UnmanagedList unmanaged; DeletedList deleted; ClientList unconstrained_stacking_order; // Topmost last ClientList stacking_order; // Topmost last bool force_restacking; mutable ToplevelList x_stacking; // From XQueryTree() mutable bool x_stacking_dirty; QVector< ClientList > focus_chain; // Currently ative last ClientList global_focus_chain; // This one is only for things like tabbox's MRU ClientList should_get_focus; // Last is most recent ClientList attention_chain; bool showing_desktop; ClientList showing_desktop_clients; int block_showing_desktop; GroupList groups; bool was_user_interaction; bool session_saving; int session_active_client; int session_desktop; bool control_grab; bool tab_grab; //KKeyNative walkThroughDesktopsKeycode, walkBackThroughDesktopsKeycode; //KKeyNative walkThroughDesktopListKeycode, walkBackThroughDesktopListKeycode; //KKeyNative walkThroughWindowsKeycode, walkBackThroughWindowsKeycode; KShortcut cutWalkThroughDesktops, cutWalkThroughDesktopsReverse; KShortcut cutWalkThroughDesktopList, cutWalkThroughDesktopListReverse; KShortcut cutWalkThroughWindows, cutWalkThroughWindowsReverse; KShortcut cutWalkThroughGroupWindows, cutWalkThroughGroupWindowsReverse; KShortcut cutWalkThroughWindowsAlternative, cutWalkThroughWindowsAlternativeReverse; bool mouse_emulation; unsigned int mouse_emulation_state; WId mouse_emulation_window; int block_focus; TabBox::TabBox* tab_box; DesktopChangeOSD* desktop_change_osd; QMenu* popup; QMenu* advanced_popup; QMenu* trans_popup; QActionGroup* trans_popup_group; QMenu* desk_popup; QMenu* activity_popup; QMenu* add_tabs_popup; // Menu to add the group to other group QMenu* switch_to_tab_popup; // Menu to change tab void modalActionsSwitch( bool enabled ); KActionCollection* keys; KActionCollection* client_keys; QAction* mResizeOpAction; QAction* mMoveOpAction; QAction* mMaximizeOpAction; QAction* mShadeOpAction; QAction* mTilingStateOpAction; QAction* mKeepAboveOpAction; QAction* mKeepBelowOpAction; QAction* mFullScreenOpAction; QAction* mNoBorderOpAction; QAction* mMinimizeOpAction; QAction* mCloseOpAction; QAction* mRemoveTabGroup; // Remove client from group QAction* mCloseGroup; // Close all clients in the group ShortcutDialog* client_keys_dialog; Client* client_keys_client; KActionCollection* disable_shortcuts_keys; bool global_shortcuts_disabled; bool global_shortcuts_disabled_for_client; void initAddToTabGroup(); // Load options for menu add_tabs_popup void initSwitchToTab(); // Load options for menu switch_to_tab_popup PluginMgr* mgr; RootInfo* rootInfo; QWidget* supportWindow; // Swallowing QStringList doNotManageList; // Colormap handling Colormap default_colormap; Colormap installed_colormap; // Timer to collect requests for 'reconfigure' QTimer reconfigureTimer; QTimer updateToolWindowsTimer; static Workspace* _self; bool workspaceInit; KStartupInfo* startup; ElectricBorder electric_current_border; Window electric_windows[ELECTRIC_COUNT]; int electricLeft; int electricRight; int electricTop; int electricBottom; Time electric_time_first; Time electric_time_last; Time electric_time_last_trigger; QPoint electric_push_point; int electric_reserved[ELECTRIC_COUNT]; // Corners/edges used by something Placement* initPositioning; QVector workarea; // Array of workareas for virtual desktops // Array of restricted areas that window cannot be moved into QVector restrictedmovearea; // Array of the previous restricted areas that window cannot be moved into QVector oldrestrictedmovearea; QVector< QVector > screenarea; // Array of workareas per xinerama screen for all virtual desktops bool managing_topmenus; KSelectionOwner* topmenu_selection; KSelectionWatcher* topmenu_watcher; ClientList topmenus; // Doesn't own them mutable int topmenu_height; QWidget* topmenu_space; int set_active_client_recursion; int block_stacking_updates; // When > 0, stacking updates are temporarily disabled bool blocked_propagating_new_clients; // Propagate also new clients after enabling stacking updates? Window null_focus_window; bool forced_global_mouse_grab; friend class StackingUpdatesBlocker; KSelectionOwner* cm_selection; bool compositingSuspended; QTimer compositeTimer; QTime lastCompositePaint; QTime nextPaintReference; QTimer mousePollingTimer; int compositeRate; int xrrRefreshRate; // used only for compositing QRegion repaints_region; Window overlay; // XComposite overlay window bool overlay_visible; bool overlay_shown; // For showOverlay() QRegion overlay_shape; QSlider* transSlider; QPushButton* transButton; QTimer unredirectTimer; bool forceUnredirectCheck; QList< int > composite_paint_times; QTimer compositeResetTimer; // for compressing composite resets Window outline_left; Window outline_right; Window outline_top; Window outline_bottom; private: friend bool performTransiencyCheck(); }; /** * Helper for Workspace::blockStackingUpdates() being called in pairs (True/false) */ class StackingUpdatesBlocker { public: StackingUpdatesBlocker( Workspace* w ) : ws( w ) { ws->blockStackingUpdates( true ); } ~StackingUpdatesBlocker() { ws->blockStackingUpdates( false ); } private: Workspace* ws; }; /** * NET WM Protocol handler class */ class RootInfo : public NETRootInfo { private: typedef KWin::Client Client; // Because of NET::Client public: RootInfo( Workspace* ws, Display* dpy, Window w, const char* name, unsigned long pr[], int pr_num, int scr= -1 ); protected: virtual void changeNumberOfDesktops( int n ); virtual void changeCurrentDesktop( int d ); virtual void changeActiveWindow( Window w,NET::RequestSource src, Time timestamp, Window active_window ); virtual void closeWindow( Window w ); virtual void moveResize( Window w, int x_root, int y_root, unsigned long direction ); virtual void moveResizeWindow( Window w, int flags, int x, int y, int width, int height ); virtual void gotPing( Window w, Time timestamp ); virtual void restackWindow( Window w, RequestSource source, Window above, int detail, Time timestamp ); virtual void gotTakeActivity( Window w, Time timestamp, long flags ); virtual void changeShowingDesktop( bool showing ); private: Workspace* workspace; }; //--------------------------------------------------------- // Desktop layout inline int Workspace::numberOfDesktops() const { return desktopCount_; } inline QSize Workspace::desktopGridSize() const { return desktopGridSize_; } inline int Workspace::desktopGridWidth() const { return desktopGridSize_.width(); } inline int Workspace::desktopGridHeight() const { return desktopGridSize_.height(); } inline int Workspace::workspaceWidth() const { return desktopGridSize_.width() * displayWidth(); } inline int Workspace::workspaceHeight() const { return desktopGridSize_.height() * displayHeight(); } inline int Workspace::currentDesktop() const { return currentDesktop_; } inline int Workspace::desktopAtCoords( QPoint coords ) const { return desktopGrid_[coords.y() * desktopGridSize_.width() + coords.x()]; } inline bool Workspace::isDesktopLayoutDynamic() const { return desktopLayoutDynamicity_; } inline void Workspace::setDesktopLayoutDynamicity( bool dynamicity ) { desktopLayoutDynamicity_ = dynamicity; } //--------------------------------------------------------- // Unsorted inline bool Workspace::initializing() const { return workspaceInit; } inline Client* Workspace::activeClient() const { return active_client; } inline Client* Workspace::mostRecentlyActivatedClient() const { return should_get_focus.count() > 0 ? should_get_focus.last() : active_client; } inline void Workspace::addGroup( Group* group, allowed_t ) { emit groupAdded(group); groups.append( group ); } inline void Workspace::removeGroup( Group* group, allowed_t ) { groups.removeAll( group ); } inline const ClientList& Workspace::stackingOrder() const { // TODO: Q_ASSERT( block_stacking_updates == 0 ); return stacking_order; } inline void Workspace::showWindowMenu( QPoint pos, Client* cl ) { showWindowMenu( QRect( pos, pos ), cl ); } inline void Workspace::showWindowMenu( int x, int y, Client* cl ) { showWindowMenu( QRect( QPoint( x, y ), QPoint( x, y )), cl ); } inline void Workspace::setWasUserInteraction() { was_user_interaction = true; } inline bool Workspace::wasUserInteraction() const { return was_user_interaction; } inline bool Workspace::managingTopMenus() const { return managing_topmenus; } inline void Workspace::sessionSaveStarted() { session_saving = true; } -inline void Workspace::sessionSaveDone() - { - session_saving = false; - } - inline bool Workspace::sessionSaving() const { return session_saving; } inline bool Workspace::forcedGlobalMouseGrab() const { return forced_global_mouse_grab; } inline bool Workspace::showingDesktop() const { return showing_desktop; } inline bool Workspace::globalShortcutsDisabled() const { return global_shortcuts_disabled || global_shortcuts_disabled_for_client; } inline Window Workspace::overlayWindow() { return overlay; } inline bool Workspace::rulesUpdatesDisabled() const { return rules_updates_disabled; } inline void Workspace::forceRestacking() { force_restacking = true; StackingUpdatesBlocker blocker( this ); // Do restacking if not blocked } inline void Workspace::updateFocusMousePosition( const QPoint& pos ) { focusMousePos = pos; } inline QPoint Workspace::focusMousePosition() const { return focusMousePos; } template< typename T > inline Client* Workspace::findClient( T predicate ) const { if( Client* ret = findClientInList( clients, predicate )) return ret; if( Client* ret = findClientInList( desktops, predicate )) return ret; return NULL; } template< typename T1, typename T2 > inline void Workspace::forEachClient( T1 procedure, T2 predicate ) { for( ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it ) if( predicate( const_cast( *it ))) procedure( *it ); for( ClientList::ConstIterator it = desktops.constBegin(); it != desktops.constEnd(); ++it ) if( predicate( const_cast( *it ))) procedure( *it ); } template< typename T > inline void Workspace::forEachClient( T procedure ) { return forEachClient( procedure, TruePredicate() ); } template< typename T > inline Unmanaged* Workspace::findUnmanaged( T predicate ) const { return findUnmanagedInList( unmanaged, predicate ); } template< typename T1, typename T2 > inline void Workspace::forEachUnmanaged( T1 procedure, T2 predicate ) { for( UnmanagedList::ConstIterator it = unmanaged.constBegin(); it != unmanaged.constEnd(); ++it ) if( predicate( const_cast( *it ))) procedure( *it ); } template< typename T > inline void Workspace::forEachUnmanaged( T procedure ) { return forEachUnmanaged( procedure, TruePredicate() ); } KWIN_COMPARE_PREDICATE( ClientMatchPredicate, Client, const Client*, cl == value ); inline bool Workspace::hasClient( const Client* c ) { return findClient( ClientMatchPredicate( c )); } inline void Workspace::checkCompositeTimer() { if( !compositeTimer.isActive() ) setCompositeTimer(); } inline bool Workspace::hasDecorationShadows() const { return mgr->factory()->supports( AbilityProvidesShadow ); } inline bool Workspace::decorationHasAlpha() const { return mgr->factory()->supports( AbilityUsesAlphaChannel ); } inline bool Workspace::decorationSupportsClientGrouping() const { return mgr->factory()->supports( AbilityClientGrouping ); } inline bool Workspace::decorationSupportsFrameOverlap() const { return mgr->factory()->supports( AbilityExtendIntoClientArea ); } inline bool Workspace::decorationSupportsBlurBehind() const { return mgr->factory()->supports( AbilityUsesBlurBehind ); } inline void Workspace::addClientGroup( ClientGroup* group ) { clientGroups.append( group ); } inline void Workspace::removeClientGroup( ClientGroup* group ) { clientGroups.removeAll( group ); } /* * Called from D-BUS */ inline void Workspace::toggleTiling() { slotToggleTiling(); } /* * Called from D-BUS */ inline void Workspace::nextTileLayout() { slotNextTileLayout(); } /* * Called from D-BUS */ inline void Workspace::previousTileLayout() { slotPreviousTileLayout(); } } // namespace #endif