diff --git a/client.cpp b/client.cpp index 90d78cd29..6b3052f80 100644 --- a/client.cpp +++ b/client.cpp @@ -1,2262 +1,2262 @@ /******************************************************************** 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 ) { // 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(); workspace()->updateMinimizedOfTransients( this ); 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(); 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; } /** * 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 { 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 ); 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( kdbgstream& stream ) const +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(','); 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 ); } } // namespace #include "client.moc" diff --git a/client.h b/client.h index 4e5778492..f8d67d337 100644 --- a/client.h +++ b/client.h @@ -1,998 +1,998 @@ /******************************************************************** 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; } 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( kdbgstream& stream ) const; + 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(); }; /** * 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/deleted.cpp b/deleted.cpp index 6c1297ff0..91cc454d6 100644 --- a/deleted.cpp +++ b/deleted.cpp @@ -1,155 +1,155 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 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 "deleted.h" #include "workspace.h" #include "client.h" #include "effects.h" namespace KWin { Deleted::Deleted( Workspace* ws ) : Toplevel( ws ) , delete_refcount( 1 ) , no_border( true ) , padding_left( 0 ) , padding_top( 0 ) , padding_right( 0 ) , padding_bottom( 0 ) { } Deleted::~Deleted() { if( delete_refcount != 0 ) kError(1212) << "Deleted client has non-zero reference count (" << delete_refcount << ")"; assert( delete_refcount == 0 ); workspace()->removeDeleted( this, Allowed ); deleteEffectWindow(); } Deleted* Deleted::create( Toplevel* c ) { Deleted* d = new Deleted( c->workspace()); d->copyToDeleted( c ); d->workspace()->addDeleted( d, Allowed ); return d; } // to be used only from Workspace::finishCompositing() void Deleted::discard( allowed_t ) { delete_refcount = 0; delete this; } void Deleted::copyToDeleted( Toplevel* c ) { assert( dynamic_cast< Deleted* >( c ) == NULL ); Toplevel::copyToDeleted( c ); desk = c->desktop(); activityList = c->activities(); contentsRect = QRect( c->clientPos(), c->clientSize()); transparent_rect = c->transparentRect(); if( WinInfo* cinfo = dynamic_cast< WinInfo* >( info )) cinfo->disable(); Client* client = dynamic_cast(c); if( client ) { no_border = client->noBorder(); padding_left = client->paddingLeft(); padding_right = client->paddingRight(); padding_bottom = client->paddingBottom(); padding_top = client->paddingTop(); if( !no_border ) { client->layoutDecorationRects(decoration_left, decoration_top, decoration_right, decoration_bottom, Client::WindowRelative); decorationPixmapLeft = *client->leftDecoPixmap(); decorationPixmapRight = *client->rightDecoPixmap(); decorationPixmapTop = *client->topDecoPixmap(); decorationPixmapBottom = *client->bottomDecoPixmap(); } } } void Deleted::unrefWindow( bool delay ) { if( --delete_refcount > 0 ) return; // needs to be delayed when calling from effects, otherwise it'd be rather // complicated to handle the case of the window going away during a painting pass if( delay ) deleteLater(); else delete this; } int Deleted::desktop() const { return desk; } QStringList Deleted::activities() const { return activityList; } QPoint Deleted::clientPos() const { return contentsRect.topLeft(); } QSize Deleted::clientSize() const { return contentsRect.size(); } -void Deleted::debug( kdbgstream& stream ) const +void Deleted::debug( QDebug& stream ) const { stream << "\'ID:" << window() << "\' (deleted)"; } void Deleted::layoutDecorationRects(QRect& left, QRect& top, QRect& right, QRect& bottom) const { left = decoration_left; top = decoration_top; right = decoration_right; bottom = decoration_bottom; } QRect Deleted::decorationRect() const { return rect().adjusted(-padding_left, -padding_top, padding_top, padding_bottom); } QRect Deleted::transparentRect() const { return transparent_rect; } } // namespace #include "deleted.moc" diff --git a/deleted.h b/deleted.h index e7f70b151..779471d8a 100644 --- a/deleted.h +++ b/deleted.h @@ -1,85 +1,85 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 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_DELETED_H #define KWIN_DELETED_H #include "toplevel.h" namespace KWin { class Deleted : public Toplevel { Q_OBJECT public: static Deleted* create( Toplevel* c ); // used by effects to keep the window around for e.g. fadeout effects when it's destroyed void refWindow(); void unrefWindow( bool delay = false ); void discard( allowed_t ); virtual int desktop() const; virtual QStringList activities() const; virtual QPoint clientPos() const; virtual QSize clientSize() const; virtual QRect transparentRect() const; const QPixmap *topDecoPixmap() const { return &decorationPixmapTop; } const QPixmap *leftDecoPixmap() const { return &decorationPixmapLeft; } const QPixmap *bottomDecoPixmap() const { return &decorationPixmapBottom; } const QPixmap *rightDecoPixmap() const { return &decorationPixmapRight; } bool noBorder() const { return no_border; } void layoutDecorationRects(QRect &left, QRect &top, QRect &right, QRect &bottom) const; virtual void addRepaintFull(); // in composite.cpp QRect decorationRect() const; protected: - virtual void debug( kdbgstream& stream ) const; + virtual void debug( QDebug& stream ) const; virtual bool shouldUnredirect() const; private: Deleted( Workspace *ws ); // use create() void copyToDeleted( Toplevel* c ); virtual ~Deleted(); // deleted only using unrefWindow() int delete_refcount; double window_opacity; int desk; QStringList activityList; QRect contentsRect; // for clientPos()/clientSize() QRect transparent_rect; QPixmap decorationPixmapLeft; QPixmap decorationPixmapRight; QPixmap decorationPixmapTop; QPixmap decorationPixmapBottom; bool no_border; QRect decoration_left; QRect decoration_right; QRect decoration_top; QRect decoration_bottom; int padding_left, padding_top, padding_right, padding_bottom; }; inline void Deleted::refWindow() { ++delete_refcount; } } // namespace #endif diff --git a/rules.cpp b/rules.cpp index 5ea0b6a83..324c7cd68 100644 --- a/rules.cpp +++ b/rules.cpp @@ -1,1081 +1,1081 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2004 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 "rules.h" #include #include #include #include #include #include #ifndef KCMRULES #include "client.h" #include "workspace.h" #endif namespace KWin { Rules::Rules() : temporary_state( 0 ) , wmclassmatch( UnimportantMatch ) , wmclasscomplete( UnimportantMatch ) , windowrolematch( UnimportantMatch ) , titlematch( UnimportantMatch ) , extrarolematch( UnimportantMatch ) , clientmachinematch( UnimportantMatch ) , types( NET::AllTypesMask ) , placementrule( UnusedForceRule ) , positionrule( UnusedSetRule ) , sizerule( UnusedSetRule ) , minsizerule( UnusedForceRule ) , maxsizerule( UnusedForceRule ) , opacityactiverule( UnusedForceRule ) , opacityinactiverule( UnusedForceRule ) , tilingoptionrule( UnusedForceRule ) , ignorepositionrule( UnusedForceRule ) , desktoprule( UnusedSetRule ) , typerule( UnusedForceRule ) , maximizevertrule( UnusedSetRule ) , maximizehorizrule( UnusedSetRule ) , minimizerule( UnusedSetRule ) , shaderule( UnusedSetRule ) , skiptaskbarrule( UnusedSetRule ) , skippagerrule( UnusedSetRule ) , skipswitcherrule( UnusedSetRule ) , aboverule( UnusedSetRule ) , belowrule( UnusedSetRule ) , fullscreenrule( UnusedSetRule ) , noborderrule( UnusedSetRule ) , fsplevelrule( UnusedForceRule ) , acceptfocusrule( UnusedForceRule ) , moveresizemoderule( UnusedForceRule ) , closeablerule( UnusedForceRule ) , autogrouprule( UnusedForceRule ) , autogroupfgrule( UnusedForceRule ) , autogroupidrule( UnusedForceRule ) , strictgeometryrule( UnusedForceRule ) , shortcutrule( UnusedSetRule ) , disableglobalshortcutsrule( UnusedForceRule ) { } Rules::Rules( const QString& str, bool temporary ) : temporary_state( temporary ? 2 : 0 ) { KTemporaryFile file; if( file.open() ) { QByteArray s = str.toUtf8(); file.write( s.data(), s.length()); } file.flush(); KConfig cfg( file.fileName(), KConfig::SimpleConfig); readFromCfg( cfg.group( QString() ) ); if( description.isEmpty()) description = "temporary"; } #define READ_MATCH_STRING( var, func ) \ var = cfg.readEntry( #var ) func; \ var##match = (StringMatch) qMax( FirstStringMatch, \ qMin( LastStringMatch, static_cast< StringMatch >( cfg.readEntry( #var "match",0 )))); #define READ_SET_RULE( var, func, def ) \ var = func ( cfg.readEntry( #var, def)); \ var##rule = readSetRule( cfg, #var "rule" ); #define READ_SET_RULE_DEF( var , func, def ) \ var = func ( cfg.readEntry( #var, def )); \ var##rule = readSetRule( cfg, #var "rule" ); #define READ_FORCE_RULE( var, func, def) \ var = func ( cfg.readEntry( #var, def)); \ var##rule = readForceRule( cfg, #var "rule" ); #define READ_FORCE_RULE2( var, def, func, funcarg ) \ var = func ( cfg.readEntry( #var, def),funcarg ); \ var##rule = readForceRule( cfg, #var "rule" ); Rules::Rules( const KConfigGroup& cfg ) : temporary_state( 0 ) { readFromCfg( cfg ); } static int limit0to4( int i ) { return qMax( 0, qMin( 4, i )); } void Rules::readFromCfg( const KConfigGroup& cfg ) { description = cfg.readEntry( "Description" ); if( description.isEmpty()) // capitalized first, lowercase for backwards compatibility description = cfg.readEntry( "description" ); READ_MATCH_STRING( wmclass, .toLower().toLatin1() ); wmclasscomplete = cfg.readEntry( "wmclasscomplete" , false); READ_MATCH_STRING( windowrole, .toLower().toLatin1() ); READ_MATCH_STRING( title, ); READ_MATCH_STRING( extrarole, .toLower().toLatin1() ); READ_MATCH_STRING( clientmachine, .toLower().toLatin1() ); types = cfg.readEntry( "types", uint(NET::AllTypesMask) ); READ_FORCE_RULE2( placement,QString(), Placement::policyFromString,false ); READ_SET_RULE_DEF( position, , invalidPoint ); READ_SET_RULE( size,, QSize()); if( size.isEmpty() && sizerule != ( SetRule )Remember) sizerule = UnusedSetRule; READ_FORCE_RULE( minsize,, QSize()); if( !minsize.isValid()) minsize = QSize( 1, 1 ); READ_FORCE_RULE( maxsize, , QSize()); if( maxsize.isEmpty()) maxsize = QSize( 32767, 32767 ); READ_FORCE_RULE( opacityactive, , 0); if( opacityactive < 0 || opacityactive > 100 ) opacityactive = 100; READ_FORCE_RULE( opacityinactive,, 0); if( opacityinactive < 0 || opacityinactive > 100 ) opacityinactive = 100; READ_FORCE_RULE( tilingoption,, 0 ); READ_FORCE_RULE( ignoreposition,, false); READ_SET_RULE( desktop,,0 ); type = readType( cfg, "type" ); typerule = type != NET::Unknown ? readForceRule( cfg, "typerule" ) : UnusedForceRule; READ_SET_RULE( maximizevert,, false); READ_SET_RULE( maximizehoriz,, false); READ_SET_RULE( minimize,, false); READ_SET_RULE( shade,, false); READ_SET_RULE( skiptaskbar,, false); READ_SET_RULE( skippager,, false); READ_SET_RULE( skipswitcher,, false); READ_SET_RULE( above,, false); READ_SET_RULE( below,, false); READ_SET_RULE( fullscreen,, false); READ_SET_RULE( noborder,,false ); READ_FORCE_RULE( fsplevel,limit0to4,0 ); // fsp is 0-4 READ_FORCE_RULE( acceptfocus, , false); READ_FORCE_RULE( moveresizemode,Options::stringToMoveResizeMode, QString()); READ_FORCE_RULE( closeable, , false); READ_FORCE_RULE( autogroup,, false); READ_FORCE_RULE( autogroupfg,, true); READ_FORCE_RULE( autogroupid,, QString()); READ_FORCE_RULE( strictgeometry, , false); READ_SET_RULE( shortcut, ,QString() ); READ_FORCE_RULE( disableglobalshortcuts, , false); } #undef READ_MATCH_STRING #undef READ_SET_RULE #undef READ_FORCE_RULE #undef READ_FORCE_RULE2 #define WRITE_MATCH_STRING( var, cast, force ) \ if( !var.isEmpty() || force ) \ { \ cfg.writeEntry( #var, cast var ); \ cfg.writeEntry( #var "match", (int)var##match ); \ } \ else \ { \ cfg.deleteEntry( #var ); \ cfg.deleteEntry( #var "match" ); \ } #define WRITE_SET_RULE( var, func ) \ if( var##rule != UnusedSetRule ) \ { \ cfg.writeEntry( #var, func ( var )); \ cfg.writeEntry( #var "rule", (int)var##rule ); \ } \ else \ { \ cfg.deleteEntry( #var ); \ cfg.deleteEntry( #var "rule" ); \ } #define WRITE_FORCE_RULE( var, func ) \ if( var##rule != UnusedForceRule ) \ { \ cfg.writeEntry( #var, func ( var )); \ cfg.writeEntry( #var "rule", (int)var##rule ); \ } \ else \ { \ cfg.deleteEntry( #var ); \ cfg.deleteEntry( #var "rule" ); \ } void Rules::write( KConfigGroup& cfg ) const { cfg.writeEntry( "Description", description ); // always write wmclass WRITE_MATCH_STRING( wmclass, (const char*), true ); cfg.writeEntry( "wmclasscomplete", wmclasscomplete ); WRITE_MATCH_STRING( windowrole, (const char*), false ); WRITE_MATCH_STRING( title,, false ); WRITE_MATCH_STRING( extrarole, (const char*), false ); WRITE_MATCH_STRING( clientmachine, (const char*), false ); if (types != NET::AllTypesMask) cfg.writeEntry("types", uint(types)); else cfg.deleteEntry("types"); WRITE_FORCE_RULE( placement, Placement::policyToString ); WRITE_SET_RULE( position, ); WRITE_SET_RULE( size, ); WRITE_FORCE_RULE( minsize, ); WRITE_FORCE_RULE( maxsize, ); WRITE_FORCE_RULE( opacityactive, ); WRITE_FORCE_RULE( opacityinactive, ); WRITE_FORCE_RULE( tilingoption, ); WRITE_FORCE_RULE( ignoreposition, ); WRITE_SET_RULE( desktop, ); WRITE_FORCE_RULE( type, int ); WRITE_SET_RULE( maximizevert, ); WRITE_SET_RULE( maximizehoriz, ); WRITE_SET_RULE( minimize, ); WRITE_SET_RULE( shade, ); WRITE_SET_RULE( skiptaskbar, ); WRITE_SET_RULE( skippager, ); WRITE_SET_RULE( skipswitcher, ); WRITE_SET_RULE( above, ); WRITE_SET_RULE( below, ); WRITE_SET_RULE( fullscreen, ); WRITE_SET_RULE( noborder, ); WRITE_FORCE_RULE( fsplevel, ); WRITE_FORCE_RULE( acceptfocus, ); WRITE_FORCE_RULE( moveresizemode, Options::moveResizeModeToString ); WRITE_FORCE_RULE( closeable, ); WRITE_FORCE_RULE( autogroup, ); WRITE_FORCE_RULE( autogroupfg, ); WRITE_FORCE_RULE( autogroupid, ); WRITE_FORCE_RULE( strictgeometry, ); WRITE_SET_RULE( shortcut, ); WRITE_FORCE_RULE( disableglobalshortcuts, ); } #undef WRITE_MATCH_STRING #undef WRITE_SET_RULE #undef WRITE_FORCE_RULE // returns true if it doesn't affect anything bool Rules::isEmpty() const { return( placementrule == UnusedForceRule && positionrule == UnusedSetRule && sizerule == UnusedSetRule && minsizerule == UnusedForceRule && maxsizerule == UnusedForceRule && opacityactiverule == UnusedForceRule && opacityinactiverule == UnusedForceRule && tilingoptionrule == UnusedForceRule && ignorepositionrule == UnusedForceRule && desktoprule == UnusedSetRule && typerule == UnusedForceRule && maximizevertrule == UnusedSetRule && maximizehorizrule == UnusedSetRule && minimizerule == UnusedSetRule && shaderule == UnusedSetRule && skiptaskbarrule == UnusedSetRule && skippagerrule == UnusedSetRule && skipswitcherrule == UnusedSetRule && aboverule == UnusedSetRule && belowrule == UnusedSetRule && fullscreenrule == UnusedSetRule && noborderrule == UnusedSetRule && fsplevelrule == UnusedForceRule && acceptfocusrule == UnusedForceRule && moveresizemoderule == UnusedForceRule && closeablerule == UnusedForceRule && autogrouprule == UnusedForceRule && autogroupfgrule == UnusedForceRule && autogroupidrule == UnusedForceRule && strictgeometryrule == UnusedForceRule && shortcutrule == UnusedSetRule && disableglobalshortcutsrule == UnusedForceRule ); } Rules::SetRule Rules::readSetRule( const KConfigGroup& cfg, const QString& key ) { int v = cfg.readEntry( key,0 ); if( v >= DontAffect && v <= ForceTemporarily ) return static_cast< SetRule >( v ); return UnusedSetRule; } Rules::ForceRule Rules::readForceRule( const KConfigGroup& cfg, const QString& key ) { int v = cfg.readEntry( key,0 ); if( v == DontAffect || v == Force || v == ForceTemporarily ) return static_cast< ForceRule >( v ); return UnusedForceRule; } NET::WindowType Rules::readType( const KConfigGroup& cfg, const QString& key ) { int v = cfg.readEntry( key,0 ); if( v >= NET::Normal && v <= NET::Splash ) return static_cast< NET::WindowType >( v ); return NET::Unknown; } bool Rules::matchType( NET::WindowType match_type ) const { if( types != NET::AllTypesMask ) { if( match_type == NET::Unknown ) match_type = NET::Normal; // NET::Unknown->NET::Normal is only here for matching if( !NET::typeMatchesMask( match_type, types )) return false; } return true; } bool Rules::matchWMClass( const QByteArray& match_class, const QByteArray& match_name ) const { if( wmclassmatch != UnimportantMatch ) { // TODO optimize? QByteArray cwmclass = wmclasscomplete ? match_name + ' ' + match_class : match_class; if( wmclassmatch == RegExpMatch && QRegExp( wmclass ).indexIn( cwmclass ) == -1 ) return false; if( wmclassmatch == ExactMatch && wmclass != cwmclass ) return false; if( wmclassmatch == SubstringMatch && !cwmclass.contains( wmclass )) return false; } return true; } bool Rules::matchRole( const QByteArray& match_role ) const { if( windowrolematch != UnimportantMatch ) { if( windowrolematch == RegExpMatch && QRegExp( windowrole ).indexIn( match_role ) == -1 ) return false; if( windowrolematch == ExactMatch && windowrole != match_role ) return false; if( windowrolematch == SubstringMatch && !match_role.contains( windowrole )) return false; } return true; } bool Rules::matchTitle( const QString& match_title ) const { if( titlematch != UnimportantMatch ) { if( titlematch == RegExpMatch && QRegExp( title ).indexIn( match_title ) == -1 ) return false; if( titlematch == ExactMatch && title != match_title ) return false; if( titlematch == SubstringMatch && !match_title.contains( title )) return false; } return true; } bool Rules::matchClientMachine( const QByteArray& match_machine ) const { if( clientmachinematch != UnimportantMatch ) { // if it's localhost, check also "localhost" before checking hostname if( match_machine != "localhost" && isLocalMachine( match_machine ) && matchClientMachine( "localhost" )) return true; if( clientmachinematch == RegExpMatch && QRegExp( clientmachine ).indexIn( match_machine ) == -1 ) return false; if( clientmachinematch == ExactMatch && clientmachine != match_machine ) return false; if( clientmachinematch == SubstringMatch && !match_machine.contains( clientmachine )) return false; } return true; } #ifndef KCMRULES bool Rules::match( const Client* c ) const { if( !matchType( c->windowType( true ))) return false; if( !matchWMClass( c->resourceClass(), c->resourceName())) return false; if( !matchRole( c->windowRole())) return false; if( !matchTitle( c->caption( false ))) return false; // TODO extrarole if( !matchClientMachine( c->wmClientMachine( false ))) return false; return true; } bool Rules::update( Client* c ) { // TODO check this setting is for this client ? bool updated = false; if( positionrule == ( SetRule )Remember) { if( !c->isFullScreen()) { QPoint new_pos = position; // don't use the position in the direction which is maximized if(( c->maximizeMode() & MaximizeHorizontal ) == 0 ) new_pos.setX( c->pos().x()); if(( c->maximizeMode() & MaximizeVertical ) == 0 ) new_pos.setY( c->pos().y()); updated = updated || position != new_pos; position = new_pos; } } if( sizerule == ( SetRule )Remember) { if( !c->isFullScreen()) { QSize new_size = size; // don't use the position in the direction which is maximized if(( c->maximizeMode() & MaximizeHorizontal ) == 0 ) new_size.setWidth( c->size().width()); if(( c->maximizeMode() & MaximizeVertical ) == 0 ) new_size.setHeight( c->size().height()); updated = updated || size != new_size; size = new_size; } } if( desktoprule == ( SetRule )Remember) { updated = updated || desktop != c->desktop(); desktop = c->desktop(); } if( maximizevertrule == ( SetRule )Remember) { updated = updated || maximizevert != bool( c->maximizeMode() & MaximizeVertical ); maximizevert = c->maximizeMode() & MaximizeVertical; } if( maximizehorizrule == ( SetRule )Remember) { updated = updated || maximizehoriz != bool( c->maximizeMode() & MaximizeHorizontal ); maximizehoriz = c->maximizeMode() & MaximizeHorizontal; } if( minimizerule == ( SetRule )Remember) { updated = updated || minimize != c->isMinimized(); minimize = c->isMinimized(); } if( shaderule == ( SetRule )Remember) { updated = updated || ( shade != ( c->shadeMode() != ShadeNone )); shade = c->shadeMode() != ShadeNone; } if( skiptaskbarrule == ( SetRule )Remember) { updated = updated || skiptaskbar != c->skipTaskbar(); skiptaskbar = c->skipTaskbar(); } if( skippagerrule == ( SetRule )Remember) { updated = updated || skippager != c->skipPager(); skippager = c->skipPager(); } if( skipswitcherrule == ( SetRule )Remember) { updated = updated || skipswitcher != c->skipSwitcher(); skipswitcher = c->skipSwitcher(); } if( aboverule == ( SetRule )Remember) { updated = updated || above != c->keepAbove(); above = c->keepAbove(); } if( belowrule == ( SetRule )Remember) { updated = updated || below != c->keepBelow(); below = c->keepBelow(); } if( fullscreenrule == ( SetRule )Remember) { updated = updated || fullscreen != c->isFullScreen(); fullscreen = c->isFullScreen(); } if( noborderrule == ( SetRule )Remember) { updated = updated || noborder != c->noBorder(); noborder = c->noBorder(); } if (opacityactiverule == ( ForceRule )Force) { // TODO } if (opacityinactiverule == ( ForceRule )Force) { // TODO } return updated; } #define APPLY_RULE( var, name, type ) \ bool Rules::apply##name( type& arg, bool init ) const \ { \ if( checkSetRule( var##rule, init )) \ arg = this->var; \ return checkSetStop( var##rule ); \ } #define APPLY_FORCE_RULE( var, name, type ) \ bool Rules::apply##name( type& arg ) const \ { \ if( checkForceRule( var##rule )) \ arg = this->var; \ return checkForceStop( var##rule ); \ } APPLY_FORCE_RULE( placement, Placement, Placement::Policy ) bool Rules::applyGeometry( QRect& rect, bool init ) const { QPoint p = rect.topLeft(); QSize s = rect.size(); bool ret = false; // no short-circuiting if( applyPosition( p, init )) { rect.moveTopLeft( p ); ret = true; } if( applySize( s, init )) { rect.setSize( s ); ret = true; } return ret; } bool Rules::applyPosition( QPoint& pos, bool init ) const { if( this->position != invalidPoint && checkSetRule( positionrule, init )) pos = this->position; return checkSetStop( positionrule ); } bool Rules::applySize( QSize& s, bool init ) const { if( this->size.isValid() && checkSetRule( sizerule, init )) s = this->size; return checkSetStop( sizerule ); } APPLY_FORCE_RULE( minsize, MinSize, QSize ) APPLY_FORCE_RULE( maxsize, MaxSize, QSize ) APPLY_FORCE_RULE( opacityactive, OpacityActive, int ) APPLY_FORCE_RULE( opacityinactive, OpacityInactive, int ) APPLY_FORCE_RULE( ignoreposition, IgnorePosition, bool ) APPLY_FORCE_RULE( tilingoption, TilingOption, int ) // the cfg. entry needs to stay named the say for backwards compatibility bool Rules::applyIgnoreGeometry( bool& ignore ) const { return applyIgnorePosition( ignore ); } APPLY_RULE( desktop, Desktop, int ) APPLY_FORCE_RULE( type, Type, NET::WindowType ) bool Rules::applyMaximizeHoriz( MaximizeMode& mode, bool init ) const { if( checkSetRule( maximizehorizrule, init )) mode = static_cast< MaximizeMode >(( maximizehoriz ? MaximizeHorizontal : 0 ) | ( mode & MaximizeVertical )); return checkSetStop( maximizehorizrule ); } bool Rules::applyMaximizeVert( MaximizeMode& mode, bool init ) const { if( checkSetRule( maximizevertrule, init )) mode = static_cast< MaximizeMode >(( maximizevert ? MaximizeVertical : 0 ) | ( mode & MaximizeHorizontal )); return checkSetStop( maximizevertrule ); } APPLY_RULE( minimize, Minimize, bool ) bool Rules::applyShade( ShadeMode& sh, bool init ) const { if( checkSetRule( shaderule, init )) { if( !this->shade ) sh = ShadeNone; if( this->shade && sh == ShadeNone ) sh = ShadeNormal; } return checkSetStop( shaderule ); } APPLY_RULE( skiptaskbar, SkipTaskbar, bool ) APPLY_RULE( skippager, SkipPager, bool ) APPLY_RULE( skipswitcher, SkipSwitcher, bool ) APPLY_RULE( above, KeepAbove, bool ) APPLY_RULE( below, KeepBelow, bool ) APPLY_RULE( fullscreen, FullScreen, bool ) APPLY_RULE( noborder, NoBorder, bool ) APPLY_FORCE_RULE( fsplevel, FSP, int ) APPLY_FORCE_RULE( acceptfocus, AcceptFocus, bool ) APPLY_FORCE_RULE( moveresizemode, MoveResizeMode, Options::MoveResizeMode ) APPLY_FORCE_RULE( closeable, Closeable, bool ) APPLY_FORCE_RULE( autogroup, Autogrouping, bool ) APPLY_FORCE_RULE( autogroupfg, AutogroupInForeground, bool ) APPLY_FORCE_RULE( autogroupid, AutogroupById, QString ) APPLY_FORCE_RULE( strictgeometry, StrictGeometry, bool ) APPLY_RULE( shortcut, Shortcut, QString ) APPLY_FORCE_RULE( disableglobalshortcuts, DisableGlobalShortcuts, bool ) #undef APPLY_RULE #undef APPLY_FORCE_RULE bool Rules::isTemporary() const { return temporary_state > 0; } bool Rules::discardTemporary( bool force ) { if( temporary_state == 0 ) // not temporary return false; if( force || --temporary_state == 0 ) // too old { delete this; return true; } return false; } #define DISCARD_USED_SET_RULE( var ) \ do { \ if( var##rule == ( SetRule ) ApplyNow || ( withdrawn && var##rule == ( SetRule ) ForceTemporarily )) \ var##rule = UnusedSetRule; \ } while( false ) #define DISCARD_USED_FORCE_RULE( var ) \ do { \ if( withdrawn && var##rule == ( ForceRule ) ForceTemporarily ) \ var##rule = UnusedForceRule; \ } while( false ) void Rules::discardUsed( bool withdrawn ) { DISCARD_USED_FORCE_RULE( placement ); DISCARD_USED_SET_RULE( position ); DISCARD_USED_SET_RULE( size ); DISCARD_USED_FORCE_RULE( minsize ); DISCARD_USED_FORCE_RULE( maxsize ); DISCARD_USED_FORCE_RULE( opacityactive ); DISCARD_USED_FORCE_RULE( opacityinactive ); DISCARD_USED_FORCE_RULE( tilingoption ); DISCARD_USED_FORCE_RULE( ignoreposition ); DISCARD_USED_SET_RULE( desktop ); DISCARD_USED_FORCE_RULE( type ); DISCARD_USED_SET_RULE( maximizevert ); DISCARD_USED_SET_RULE( maximizehoriz ); DISCARD_USED_SET_RULE( minimize ); DISCARD_USED_SET_RULE( shade ); DISCARD_USED_SET_RULE( skiptaskbar ); DISCARD_USED_SET_RULE( skippager ); DISCARD_USED_SET_RULE( skipswitcher ); DISCARD_USED_SET_RULE( above ); DISCARD_USED_SET_RULE( below ); DISCARD_USED_SET_RULE( fullscreen ); DISCARD_USED_SET_RULE( noborder ); DISCARD_USED_FORCE_RULE( fsplevel ); DISCARD_USED_FORCE_RULE( acceptfocus ); DISCARD_USED_FORCE_RULE( moveresizemode ); DISCARD_USED_FORCE_RULE( closeable ); DISCARD_USED_FORCE_RULE( autogroup ); DISCARD_USED_FORCE_RULE( autogroupfg ); DISCARD_USED_FORCE_RULE( autogroupid ); DISCARD_USED_FORCE_RULE( strictgeometry ); DISCARD_USED_SET_RULE( shortcut ); DISCARD_USED_FORCE_RULE( disableglobalshortcuts ); } #undef DISCARD_USED_SET_RULE #undef DISCARD_USED_FORCE_RULE #endif -kdbgstream& operator<<( kdbgstream& stream, const Rules* r ) +QDebug& operator<<( QDebug& stream, const Rules* r ) { return stream << "[" << r->description << ":" << r->wmclass << "]" ; } #ifndef KCMRULES void WindowRules::discardTemporary() { QVector< Rules* >::Iterator it2 = rules.begin(); for( QVector< Rules* >::Iterator it = rules.begin(); it != rules.end(); ) { if( (*it)->discardTemporary( true )) ++it; else { *it2++ = *it++; } } rules.erase( it2, rules.end()); } void WindowRules::update( Client* c ) { bool updated = false; for( QVector< Rules* >::ConstIterator it = rules.constBegin(); it != rules.constEnd(); ++it ) if( (*it)->update( c )) // no short-circuiting here updated = true; if( updated ) Workspace::self()->rulesUpdated(); } #define CHECK_RULE( rule, type ) \ type WindowRules::check##rule( type arg, bool init ) const \ { \ if( rules.count() == 0 ) \ return arg; \ type ret = arg; \ for( QVector< Rules* >::ConstIterator it = rules.constBegin(); \ it != rules.constEnd(); \ ++it ) \ { \ if( (*it)->apply##rule( ret, init )) \ break; \ } \ return ret; \ } #define CHECK_FORCE_RULE( rule, type ) \ type WindowRules::check##rule( type arg ) const \ { \ if( rules.count() == 0 ) \ return arg; \ type ret = arg; \ for( QVector< Rules* >::ConstIterator it = rules.begin(); \ it != rules.end(); \ ++it ) \ { \ if( (*it)->apply##rule( ret )) \ break; \ } \ return ret; \ } CHECK_FORCE_RULE( Placement, Placement::Policy ) QRect WindowRules::checkGeometry( QRect rect, bool init ) const { return QRect( checkPosition( rect.topLeft(), init ), checkSize( rect.size(), init )); } CHECK_RULE( Position, QPoint ) CHECK_RULE( Size, QSize ) CHECK_FORCE_RULE( MinSize, QSize ) CHECK_FORCE_RULE( MaxSize, QSize ) CHECK_FORCE_RULE( OpacityActive, int ) CHECK_FORCE_RULE( OpacityInactive, int ) CHECK_FORCE_RULE( TilingOption, int ) CHECK_FORCE_RULE( IgnorePosition, bool ) bool WindowRules::checkIgnoreGeometry( bool ignore ) const { return checkIgnorePosition( ignore ); } CHECK_RULE( Desktop, int ) CHECK_FORCE_RULE( Type, NET::WindowType ) CHECK_RULE( MaximizeVert, KDecorationDefines::MaximizeMode ) CHECK_RULE( MaximizeHoriz, KDecorationDefines::MaximizeMode ) KDecorationDefines::MaximizeMode WindowRules::checkMaximize( MaximizeMode mode, bool init ) const { bool vert = checkMaximizeVert( mode, init ) & MaximizeVertical; bool horiz = checkMaximizeHoriz( mode, init ) & MaximizeHorizontal; return static_cast< MaximizeMode >(( vert ? MaximizeVertical : 0 ) | ( horiz ? MaximizeHorizontal : 0 )); } CHECK_RULE( Minimize, bool ) CHECK_RULE( Shade, ShadeMode ) CHECK_RULE( SkipTaskbar, bool ) CHECK_RULE( SkipPager, bool ) CHECK_RULE( SkipSwitcher, bool ) CHECK_RULE( KeepAbove, bool ) CHECK_RULE( KeepBelow, bool ) CHECK_RULE( FullScreen, bool ) CHECK_RULE( NoBorder, bool ) CHECK_FORCE_RULE( FSP, int ) CHECK_FORCE_RULE( AcceptFocus, bool ) CHECK_FORCE_RULE( MoveResizeMode, Options::MoveResizeMode ) CHECK_FORCE_RULE( Closeable, bool ) CHECK_FORCE_RULE( Autogrouping, bool ) CHECK_FORCE_RULE( AutogroupInForeground, bool ) CHECK_FORCE_RULE( AutogroupById, QString ) CHECK_FORCE_RULE( StrictGeometry, bool ) CHECK_RULE( Shortcut, QString ) CHECK_FORCE_RULE( DisableGlobalShortcuts, bool ) #undef CHECK_RULE #undef CHECK_FORCE_RULE // Client void Client::setupWindowRules( bool ignore_temporary ) { client_rules = workspace()->findWindowRules( this, ignore_temporary ); // check only after getting the rules, because there may be a rule forcing window type if( isTopMenu()) // TODO cannot have restrictions client_rules = WindowRules(); } // Applies Force, ForceTemporarily and ApplyNow rules // Used e.g. after the rules have been modified using the kcm. void Client::applyWindowRules() { // apply force rules // Placement - does need explicit update, just like some others below // Geometry : setGeometry() doesn't check rules QRect orig_geom = QRect( pos(), sizeForClientSize( clientSize())); // handle shading QRect geom = client_rules.checkGeometry( orig_geom ); if( geom != orig_geom ) setGeometry( geom ); // MinSize, MaxSize handled by Geometry // IgnorePosition setDesktop( desktop()); // Type maximize( maximizeMode()); // Minimize : functions don't check, and there are two functions if( client_rules.checkMinimize( isMinimized())) minimize(); else unminimize(); setShade( shadeMode()); setSkipTaskbar( skipTaskbar(), true ); setSkipPager( skipPager()); setSkipSwitcher( skipSwitcher()); setKeepAbove( keepAbove()); setKeepBelow( keepBelow()); setFullScreen( isFullScreen(), true ); setNoBorder( noBorder()); // FSP // AcceptFocus : if( workspace()->mostRecentlyActivatedClient() == this && !client_rules.checkAcceptFocus( true )) workspace()->activateNextClient( this ); // MoveResizeMode // Closeable QSize s = adjustedSize(); if( s != size()) resizeWithChecks( s ); // Autogrouping : Only checked on window manage // AutogroupInForeground : Only checked on window manage // AutogroupById : Only checked on window manage // StrictGeometry setShortcut( rules()->checkShortcut( shortcut().toString())); // see also Client::setActive() if( isActive()) { setOpacity( rules()->checkOpacityActive( qRound( opacity() * 100.0 )) / 100.0 ); workspace()->disableGlobalShortcutsForClient( rules()->checkDisableGlobalShortcuts( false )); } else setOpacity( rules()->checkOpacityInactive( qRound( opacity() * 100.0 )) / 100.0 ); } void Client::updateWindowRules() { if( !isManaged()) // not fully setup yet return; if( workspace()->rulesUpdatesDisabled()) return; client_rules.update( this ); } void Client::finishWindowRules() { updateWindowRules(); client_rules = WindowRules(); } // Workspace WindowRules Workspace::findWindowRules( const Client* c, bool ignore_temporary ) { QVector< Rules* > ret; for( QList< Rules* >::Iterator it = rules.begin(); it != rules.end(); ) { if( ignore_temporary && (*it)->isTemporary()) { ++it; continue; } if( (*it)->match( c )) { Rules* rule = *it; kDebug( 1212 ) << "Rule found:" << rule << ":" << c; if( rule->isTemporary()) it = rules.erase( it ); else ++it; ret.append( rule ); continue; } ++it; } return WindowRules( ret ); } void Workspace::editWindowRules( Client* c, bool whole_app ) { writeWindowRules(); QStringList args; args << "--wid" << QString::number( c->window()); if( whole_app ) args << "--whole-app"; KToolInvocation::kdeinitExec( "kwin_rules_dialog", args ); } void Workspace::loadWindowRules() { while( !rules.isEmpty()) { delete rules.front(); rules.pop_front(); } KConfig cfg( "kwinrulesrc", KConfig::NoGlobals ); int count = cfg.group("General").readEntry( "count",0 ); for( int i = 1; i <= count; ++i ) { KConfigGroup cg( &cfg, QString::number( i )); Rules* rule = new Rules( cg ); rules.append( rule ); } } void Workspace::writeWindowRules() { rulesUpdatedTimer.stop(); KConfig cfg( "kwinrulesrc", KConfig::NoGlobals ); QStringList groups = cfg.groupList(); for( QStringList::ConstIterator it = groups.constBegin(); it != groups.constEnd(); ++it ) cfg.deleteGroup( *it ); cfg.group("General").writeEntry( "count", rules.count()); int i = 1; for( QList< Rules* >::ConstIterator it = rules.constBegin(); it != rules.constEnd(); ++it ) { if( (*it)->isTemporary()) continue; KConfigGroup cg( &cfg, QString::number( i )); (*it)->write( cg ); ++i; } } void Workspace::gotTemporaryRulesMessage( const QString& message ) { bool was_temporary = false; for( QList< Rules* >::ConstIterator it = rules.constBegin(); it != rules.constEnd(); ++it ) if( (*it)->isTemporary()) was_temporary = true; Rules* rule = new Rules( message, true ); rules.prepend( rule ); // highest priority first if( !was_temporary ) QTimer::singleShot( 60000, this, SLOT( cleanupTemporaryRules())); } void Workspace::cleanupTemporaryRules() { bool has_temporary = false; for( QList< Rules* >::Iterator it = rules.begin(); it != rules.end(); ) { if( (*it)->discardTemporary( false )) it = rules.erase( it ); else { if( (*it)->isTemporary()) has_temporary = true; ++it; } } if( has_temporary ) QTimer::singleShot( 60000, this, SLOT( cleanupTemporaryRules())); } void Workspace::discardUsedWindowRules( Client* c, bool withdrawn ) { bool updated = false; for( QList< Rules* >::Iterator it = rules.begin(); it != rules.end(); ) { if( c->rules()->contains( *it )) { updated = true; (*it)->discardUsed( withdrawn ); if( (*it)->isEmpty()) { c->removeRule( *it ); Rules* r = *it; it = rules.erase( it ); delete r; continue; } } ++it; } if( updated ) rulesUpdated(); } void Workspace::rulesUpdated() { rulesUpdatedTimer.setSingleShot( true ); rulesUpdatedTimer.start( 1000 ); } void Workspace::disableRulesUpdates( bool disable ) { rules_updates_disabled = disable; if( !disable ) foreach( Client* c, clients ) c->updateWindowRules(); } #endif } // namespace diff --git a/rules.h b/rules.h index 026197dcd..9fb4f4680 100644 --- a/rules.h +++ b/rules.h @@ -1,336 +1,336 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2004 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_RULES_H #define KWIN_RULES_H #include #include #include #include #include "placement.h" #include "lib/kdecoration.h" #include "options.h" #include "utils.h" class KConfig; namespace KWin { class Client; class Rules; #ifndef KCMRULES // only for kwin core class WindowRules : public KDecorationDefines { public: WindowRules( const QVector< Rules* >& rules ); WindowRules(); void update( Client* ); void discardTemporary(); bool contains( const Rules* rule ) const; void remove( Rules* rule ); Placement::Policy checkPlacement( Placement::Policy placement ) const; QRect checkGeometry( QRect rect, bool init = false ) const; // use 'invalidPoint' with checkPosition, unlike QSize() and QRect(), QPoint() is a valid point QPoint checkPosition( QPoint pos, bool init = false ) const; QSize checkSize( QSize s, bool init = false ) const; QSize checkMinSize( QSize s ) const; QSize checkMaxSize( QSize s ) const; int checkOpacityActive(int s) const; int checkOpacityInactive(int s) const; int checkTilingOption( int s ) const; bool checkIgnoreGeometry( bool ignore ) const; int checkDesktop( int desktop, bool init = false ) const; NET::WindowType checkType( NET::WindowType type ) const; MaximizeMode checkMaximize( MaximizeMode mode, bool init = false ) const; bool checkMinimize( bool minimized, bool init = false ) const; ShadeMode checkShade( ShadeMode shade, bool init = false ) const; bool checkSkipTaskbar( bool skip, bool init = false ) const; bool checkSkipPager( bool skip, bool init = false ) const; bool checkSkipSwitcher( bool skip, bool init = false ) const; bool checkKeepAbove( bool above, bool init = false ) const; bool checkKeepBelow( bool below, bool init = false ) const; bool checkFullScreen( bool fs, bool init = false ) const; bool checkNoBorder( bool noborder, bool init = false ) const; int checkFSP( int fsp ) const; bool checkAcceptFocus( bool focus ) const; Options::MoveResizeMode checkMoveResizeMode( Options::MoveResizeMode mode ) const; bool checkCloseable( bool closeable ) const; bool checkAutogrouping( bool autogroup ) const; bool checkAutogroupInForeground( bool fg ) const; QString checkAutogroupById( QString id ) const; bool checkStrictGeometry( bool strict ) const; QString checkShortcut( QString s, bool init = false ) const; bool checkDisableGlobalShortcuts( bool disable ) const; bool checkIgnorePosition( bool ignore ) const; // obsolete private: MaximizeMode checkMaximizeVert( MaximizeMode mode, bool init ) const; MaximizeMode checkMaximizeHoriz( MaximizeMode mode, bool init ) const; QVector< Rules* > rules; }; #endif class Rules : public KDecorationDefines { public: Rules(); Rules( const KConfigGroup& ); Rules( const QString&, bool temporary ); void write( KConfigGroup& ) const; bool isEmpty() const; #ifndef KCMRULES void discardUsed( bool withdrawn ); bool match( const Client* c ) const; bool update( Client* ); bool isTemporary() const; bool discardTemporary( bool force ); // removes if temporary and forced or too old bool applyPlacement( Placement::Policy& placement ) const; bool applyGeometry( QRect& rect, bool init ) const; // use 'invalidPoint' with applyPosition, unlike QSize() and QRect(), QPoint() is a valid point bool applyPosition( QPoint& pos, bool init ) const; bool applySize( QSize& s, bool init ) const; bool applyMinSize( QSize& s ) const; bool applyMaxSize( QSize& s ) const; bool applyOpacityActive(int& s) const; bool applyOpacityInactive(int& s) const; bool applyTilingOption( int& s ) const; bool applyIgnoreGeometry( bool& ignore ) const; bool applyDesktop( int& desktop, bool init ) const; bool applyType( NET::WindowType& type ) const; bool applyMaximizeVert( MaximizeMode& mode, bool init ) const; bool applyMaximizeHoriz( MaximizeMode& mode, bool init ) const; bool applyMinimize( bool& minimized, bool init ) const; bool applyShade( ShadeMode& shade, bool init ) const; bool applySkipTaskbar( bool& skip, bool init ) const; bool applySkipPager( bool& skip, bool init ) const; bool applySkipSwitcher( bool& skip, bool init ) const; bool applyKeepAbove( bool& above, bool init ) const; bool applyKeepBelow( bool& below, bool init ) const; bool applyFullScreen( bool& fs, bool init ) const; bool applyNoBorder( bool& noborder, bool init ) const; bool applyFSP( int& fsp ) const; bool applyAcceptFocus( bool& focus ) const; bool applyMoveResizeMode( Options::MoveResizeMode& mode ) const; bool applyCloseable( bool& closeable ) const; bool applyAutogrouping( bool& autogroup ) const; bool applyAutogroupInForeground( bool& fg ) const; bool applyAutogroupById( QString& id ) const; bool applyStrictGeometry( bool& strict ) const; bool applyShortcut( QString& shortcut, bool init ) const; bool applyDisableGlobalShortcuts( bool& disable ) const; bool applyIgnorePosition( bool& ignore ) const; // obsolete private: #endif bool matchType( NET::WindowType match_type ) const; bool matchWMClass( const QByteArray& match_class, const QByteArray& match_name ) const; bool matchRole( const QByteArray& match_role ) const; bool matchTitle( const QString& match_title ) const; bool matchClientMachine( const QByteArray& match_machine ) const; // All these values are saved to the cfg file, and are also used in kstart! enum { Unused = 0, DontAffect, // use the default value Force, // force the given value Apply, // apply only after initial mapping Remember, // like apply, and remember the value when the window is withdrawn ApplyNow, // apply immediatelly, then forget the setting ForceTemporarily // apply and force until the window is withdrawn }; enum SetRule { UnusedSetRule = Unused, SetRuleDummy = 256 // so that it's at least short int }; enum ForceRule { UnusedForceRule = Unused, ForceRuleDummy = 256 // so that it's at least short int }; enum StringMatch { FirstStringMatch, UnimportantMatch = FirstStringMatch, ExactMatch, SubstringMatch, RegExpMatch, LastStringMatch = RegExpMatch }; void readFromCfg( const KConfigGroup& cfg ); static SetRule readSetRule( const KConfigGroup&, const QString& key ); static ForceRule readForceRule( const KConfigGroup&, const QString& key ); static NET::WindowType readType( const KConfigGroup&, const QString& key ); #ifndef KCMRULES static bool checkSetRule( SetRule rule, bool init ); static bool checkForceRule( ForceRule rule ); static bool checkSetStop( SetRule rule ); static bool checkForceStop( ForceRule rule ); #endif int temporary_state; // e.g. for kstart QString description; QByteArray wmclass; StringMatch wmclassmatch; bool wmclasscomplete; QByteArray windowrole; StringMatch windowrolematch; QString title; StringMatch titlematch; QByteArray extrarole; StringMatch extrarolematch; QByteArray clientmachine; StringMatch clientmachinematch; unsigned long types; // types for matching Placement::Policy placement; ForceRule placementrule; QPoint position; SetRule positionrule; QSize size; SetRule sizerule; QSize minsize; ForceRule minsizerule; QSize maxsize; ForceRule maxsizerule; int opacityactive; ForceRule opacityactiverule; int opacityinactive; ForceRule opacityinactiverule; int tilingoption; ForceRule tilingoptionrule; bool ignoreposition; ForceRule ignorepositionrule; int desktop; SetRule desktoprule; NET::WindowType type; // type for setting ForceRule typerule; bool maximizevert; SetRule maximizevertrule; bool maximizehoriz; SetRule maximizehorizrule; bool minimize; SetRule minimizerule; bool shade; SetRule shaderule; bool skiptaskbar; SetRule skiptaskbarrule; bool skippager; SetRule skippagerrule; bool skipswitcher; SetRule skipswitcherrule; bool above; SetRule aboverule; bool below; SetRule belowrule; bool fullscreen; SetRule fullscreenrule; bool noborder; SetRule noborderrule; int fsplevel; ForceRule fsplevelrule; bool acceptfocus; ForceRule acceptfocusrule; Options::MoveResizeMode moveresizemode; ForceRule moveresizemoderule; bool closeable; ForceRule closeablerule; bool autogroup; ForceRule autogrouprule; bool autogroupfg; ForceRule autogroupfgrule; QString autogroupid; ForceRule autogroupidrule; bool strictgeometry; ForceRule strictgeometryrule; QString shortcut; SetRule shortcutrule; bool disableglobalshortcuts; ForceRule disableglobalshortcutsrule; - friend kdbgstream& operator<<( kdbgstream& stream, const Rules* ); + friend QDebug& operator<<( QDebug& stream, const Rules* ); }; #ifndef KCMRULES inline bool Rules::checkSetRule( SetRule rule, bool init ) { if( rule > ( SetRule )DontAffect) // Unused or DontAffect { if( rule == ( SetRule )Force || rule == ( SetRule ) ApplyNow || rule == ( SetRule ) ForceTemporarily || init ) return true; } return false; } inline bool Rules::checkForceRule( ForceRule rule ) { return rule == ( ForceRule )Force || rule == ( ForceRule ) ForceTemporarily; } inline bool Rules::checkSetStop( SetRule rule ) { return rule != UnusedSetRule; } inline bool Rules::checkForceStop( ForceRule rule ) { return rule != UnusedForceRule; } inline WindowRules::WindowRules( const QVector< Rules* >& r ) : rules( r ) { } inline WindowRules::WindowRules() { } inline bool WindowRules::contains( const Rules* rule ) const { return qFind( rules.begin(), rules.end(), rule ) != rules.end(); } inline void WindowRules::remove( Rules* rule ) { QVector< Rules* >::Iterator pos = qFind( rules.begin(), rules.end(), rule ); if( pos != rules.end()) rules.erase( pos ); } #endif -kdbgstream& operator<<( kdbgstream& stream, const Rules* ); +QDebug& operator<<( QDebug& stream, const Rules* ); } // namespace #endif diff --git a/scene_xrender.cpp b/scene_xrender.cpp index 5b7941245..520ff8e45 100644 --- a/scene_xrender.cpp +++ b/scene_xrender.cpp @@ -1,1021 +1,1021 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak Copyright (C) 2009 Fredrik Höglund 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 is the XRender-based compositing code. The primary compositing backend is the OpenGL-based one, which should be more powerful and also possibly better documented. This backend is mostly for cases when the OpenGL backend cannot be used for some reason (insufficient performance, no usable OpenGL support at all, etc.) The plan is to keep it around as long as needed/possible, but if it proves to be too much hassle it will be dropped in the future. Docs: XRender (the protocol, but the function calls map to it): http://gitweb.freedesktop.org/?p=xorg/proto/renderproto.git;a=blob_plain;hb=HEAD;f=renderproto.txt XFixes (again, the protocol): http://gitweb.freedesktop.org/?p=xorg/proto/fixesproto.git;a=blob_plain;hb=HEAD;f=fixesproto.txt */ #include "scene_xrender.h" #ifdef KWIN_HAVE_XRENDER_COMPOSITING #include "toplevel.h" #include "client.h" #include "deleted.h" #include "effects.h" #include "kwinxrenderutils.h" #include #include #include namespace KWin { //**************************************** // SceneXrender //**************************************** // kDebug() support for the XserverRegion type struct RegionDebug { RegionDebug( XserverRegion r ) : rr( r ) {} XserverRegion rr; }; -kdbgstream& operator<<( kdbgstream& stream, RegionDebug r ) +QDebug& operator<<( QDebug& stream, RegionDebug r ) { if( r.rr == None ) return stream << "EMPTY"; int num; XRectangle* rects = XFixesFetchRegion( display(), r.rr, &num ); if( rects == NULL || num == 0 ) return stream << "EMPTY"; for( int i = 0; i < num; ++i ) stream << "[" << rects[ i ].x << "+" << rects[ i ].y << " " << rects[ i ].width << "x" << rects[ i ].height << "]"; return stream; } Picture SceneXrender::buffer = None; ScreenPaintData SceneXrender::screen_paint; SceneXrender::SceneXrender( Workspace* ws ) : Scene( ws ) , front( None ) , init_ok( false ) { if( !Extensions::renderAvailable()) { kError( 1212 ) << "No XRender extension available"; return; } if( !Extensions::fixesRegionAvailable()) { kError( 1212 ) << "No XFixes v3+ extension available"; return; } KXErrorHandler xerr; if( wspace->createOverlay()) { wspace->setupOverlay( None ); XWindowAttributes attrs; XGetWindowAttributes( display(), wspace->overlayWindow(), &attrs ); format = XRenderFindVisualFormat( display(), attrs.visual ); if( format == NULL ) { kError( 1212 ) << "Failed to find XRender format for overlay window"; return; } front = XRenderCreatePicture( display(), wspace->overlayWindow(), format, 0, NULL ); } else { // create XRender picture for the root window format = XRenderFindVisualFormat( display(), DefaultVisual( display(), DefaultScreen( display()))); if( format == NULL ) { kError( 1212 ) << "Failed to find XRender format for root window"; return; // error } XRenderPictureAttributes pa; pa.subwindow_mode = IncludeInferiors; front = XRenderCreatePicture( display(), rootWindow(), format, CPSubwindowMode, &pa ); } createBuffer(); if( xerr.error( true )) { kError( 1212 ) << "XRender compositing setup failed"; return; } if( !initting ) // see comment for opengl version { if( !selfCheck()) return; selfCheckDone = true; } init_ok = true; } SceneXrender::~SceneXrender() { if( !init_ok ) { // TODO this probably needs to clean up whatever has been created until the failure wspace->destroyOverlay(); return; } XRenderFreePicture( display(), front ); XRenderFreePicture( display(), buffer ); buffer = None; wspace->destroyOverlay(); foreach( Window* w, windows ) delete w; } bool SceneXrender::initFailed() const { return !init_ok; } // Create the compositing buffer. The root window is not double-buffered, // so it is done manually using this buffer, void SceneXrender::createBuffer() { Pixmap pixmap = XCreatePixmap( display(), rootWindow(), displayWidth(), displayHeight(), DefaultDepth( display(), DefaultScreen( display()))); buffer = XRenderCreatePicture( display(), pixmap, format, 0, 0 ); XFreePixmap( display(), pixmap ); // The picture owns the pixmap now } // Just like SceneOpenGL::selfCheck() bool SceneXrender::selfCheck() { QRegion reg = selfCheckRegion(); if( wspace->overlayWindow()) { // avoid covering the whole screen too soon wspace->setOverlayShape( reg ); wspace->showOverlay(); } selfCheckSetup(); flushBuffer( PAINT_SCREEN_REGION, reg ); bool ok = selfCheckFinish(); if( wspace->overlayWindow()) wspace->hideOverlay(); return ok; } void SceneXrender::selfCheckSetup() { KXErrorHandler err; QImage img( selfCheckWidth(), selfCheckHeight(), QImage::Format_RGB32 ); img.setPixel( 0, 0, QColor( Qt::red ).rgb()); img.setPixel( 1, 0, QColor( Qt::green ).rgb()); img.setPixel( 2, 0, QColor( Qt::blue ).rgb()); img.setPixel( 0, 1, QColor( Qt::white ).rgb()); img.setPixel( 1, 1, QColor( Qt::black ).rgb()); img.setPixel( 2, 1, QColor( Qt::white ).rgb()); QPixmap pix = QPixmap::fromImage( img ); foreach( const QPoint& p, selfCheckPoints()) { XSetWindowAttributes wa; wa.override_redirect = True; ::Window window = XCreateWindow( display(), rootWindow(), 0, 0, selfCheckWidth(), selfCheckHeight(), 0, QX11Info::appDepth(), CopyFromParent, CopyFromParent, CWOverrideRedirect, &wa ); XSetWindowBackgroundPixmap( display(), window, pix.handle()); XClearWindow( display(), window ); XMapWindow( display(), window ); // move the window one down to where the result will be rendered too, just in case // the render would fail completely and eventual check would try to read this window's contents XMoveWindow( display(), window, p.x() + 1, p.y()); XCompositeRedirectWindow( display(), window, CompositeRedirectAutomatic ); Pixmap wpix = XCompositeNameWindowPixmap( display(), window ); XWindowAttributes attrs; XGetWindowAttributes( display(), window, &attrs ); XRenderPictFormat* format = XRenderFindVisualFormat( display(), attrs.visual ); Picture pic = XRenderCreatePicture( display(), wpix, format, 0, 0 ); QRect rect( p.x(), p.y(), selfCheckWidth(), selfCheckHeight()); XRenderComposite( display(), PictOpSrc, pic, None, buffer, 0, 0, 0, 0, rect.x(), rect.y(), rect.width(), rect.height()); XRenderFreePicture( display(), pic ); XFreePixmap( display(), wpix ); XDestroyWindow( display(), window ); } err.error( true ); // just sync and discard } bool SceneXrender::selfCheckFinish() { KXErrorHandler err; bool ok = true; foreach( const QPoint& p, selfCheckPoints()) { QPixmap pix = QPixmap::grabWindow( rootWindow(), p.x(), p.y(), selfCheckWidth(), selfCheckHeight()); QImage img = pix.toImage(); // kDebug(1212) << "P:" << QColor( img.pixel( 0, 0 )).name(); // kDebug(1212) << "P:" << QColor( img.pixel( 1, 0 )).name(); // kDebug(1212) << "P:" << QColor( img.pixel( 2, 0 )).name(); // kDebug(1212) << "P:" << QColor( img.pixel( 0, 1 )).name(); // kDebug(1212) << "P:" << QColor( img.pixel( 1, 1 )).name(); // kDebug(1212) << "P:" << QColor( img.pixel( 2, 1 )).name(); if( img.pixel( 0, 0 ) != QColor( Qt::red ).rgb() || img.pixel( 1, 0 ) != QColor( Qt::green ).rgb() || img.pixel( 2, 0 ) != QColor( Qt::blue ).rgb() || img.pixel( 0, 1 ) != QColor( Qt::white ).rgb() || img.pixel( 1, 1 ) != QColor( Qt::black ).rgb() || img.pixel( 2, 1 ) != QColor( Qt::white ).rgb()) { kError( 1212 ) << "XRender compositing self-check failed, disabling compositing."; ok = false; break; } } if( err.error( true )) ok = false; if( ok ) kDebug( 1212 ) << "XRender compositing self-check passed."; if( !ok && options->disableCompositingChecks ) { kWarning( 1212 ) << "Compositing checks disabled, proceeding regardless of self-check failure."; return true; } return ok; } // the entry point for painting void SceneXrender::paint( QRegion damage, ToplevelList toplevels ) { foreach( Toplevel* c, toplevels ) { assert( windows.contains( c )); stacking_order.append( windows[ c ] ); } int mask = 0; paintScreen( &mask, &damage ); if( wspace->overlayWindow()) // show the window only after the first pass, since wspace->showOverlay(); // that pass may take long if( !selfCheckDone ) { selfCheckSetup(); damage |= selfCheckRegion(); } flushBuffer( mask, damage ); if( !selfCheckDone ) { if( !selfCheckFinish()) QTimer::singleShot( 0, Workspace::self(), SLOT( finishCompositing())); selfCheckDone = true; } // do cleanup stacking_order.clear(); } void SceneXrender::flushBuffer( int mask, QRegion damage ) { if( mask & PAINT_SCREEN_REGION ) { // Use the damage region as the clip region for the root window XserverRegion front_region = toXserverRegion( damage ); XFixesSetPictureClipRegion( display(), front, 0, 0, front_region ); XFixesDestroyRegion( display(), front_region ); // copy composed buffer to the root window XFixesSetPictureClipRegion( display(), buffer, 0, 0, None ); XRenderComposite( display(), PictOpSrc, buffer, None, front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight()); XFixesSetPictureClipRegion( display(), front, 0, 0, None ); XFlush( display()); } else { // copy composed buffer to the root window XRenderComposite( display(), PictOpSrc, buffer, None, front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight()); XFlush( display()); } } void SceneXrender::paintGenericScreen( int mask, ScreenPaintData data ) { screen_paint = data; // save, transformations will be done when painting windows if( true ) // as long as paintTransformedScreen() doesn't work properly Scene::paintGenericScreen( mask, data ); else paintTransformedScreen( mask ); } /* TODO currently broken Try to do optimized painting even with transformations. Since only scaling and translation are supported by the painting code, clipping can be done manually to avoid having to paint everything in every pass. Whole screen still need to be painted but e.g. obscured windows don't. So this below is basically paintSimpleScreen() with extra code to compute clipping correctly. This code assumes that the only transformations possible with XRender are those provided by Window/ScreenPaintData, In the (very unlikely?) case more is needed then paintGenericScreen() needs to be used. */ void SceneXrender::paintTransformedScreen( int orig_mask ) { QRegion region( 0, 0, displayWidth(), displayHeight()); QList< Phase2Data > phase2; QRegion allclips; // Draw each opaque window top to bottom, subtracting the bounding rect of // each window from the clip region after it's been drawn. for( int i = stacking_order.count() - 1; // top to bottom i >= 0; --i ) { Window* w = static_cast< Window* >( stacking_order[ i ] ); WindowPrePaintData data; data.mask = orig_mask | ( w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT ); w->resetPaintingEnabled(); data.paint = region; // TODO this is wrong, transformedShape() should be used here, but is not known yet data.clip = w->isOpaque() ? region : QRegion(); data.quads = w->buildQuads(); // preparation step effects->prePaintWindow( effectWindow( w ), data, time_diff ); #ifndef NDEBUG foreach( const WindowQuad &q, data.quads ) if( q.isTransformed()) kFatal( 1212 ) << "Pre-paint calls are not allowed to transform quads!" ; #endif if( !w->isPaintingEnabled()) continue; data.paint -= allclips; // make sure to avoid already clipped areas if( data.paint.isEmpty()) // completely clipped continue; if( data.paint != region ) // prepaint added area to draw { region |= data.paint; // make sure other windows in that area get painted too painted_region |= data.paint; // make sure it makes it to the screen } // If the window is transparent, the transparent part will be done // in the 2nd pass. if( data.mask & PAINT_WINDOW_TRANSLUCENT ) phase2.prepend( Phase2Data( w, data.paint, data.clip, data.mask, data.quads )); if( data.mask & PAINT_WINDOW_OPAQUE ) { w->setTransformedShape( QRegion()); paintWindow( w, data.mask, data.paint, data.quads ); // The window can clip by its opaque parts the windows below. region -= w->transformedShape(); } // translucency or window transformed require window pixmap w->suspendUnredirect( data.mask & ( PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_TRANSFORMED )); } if( !( orig_mask & PAINT_SCREEN_BACKGROUND_FIRST )) paintBackground( region ); // Fill any areas of the root window not covered by windows // Now walk the list bottom to top, drawing translucent windows. // That we draw bottom to top is important now since we're drawing translucent objects // and also are clipping only by opaque windows. QRegion add_paint; foreach( const Phase2Data &d, phase2 ) { Scene::Window* w = d.window; paintWindow( w, d.mask, d.region | add_paint, d.quads ); // It is necessary to also add paint regions of windows below, because their // pre-paint's might have extended the paint area, so those areas need to be painted too. add_paint |= d.region; } } // fill the screen background void SceneXrender::paintBackground( QRegion region ) { PaintClipper pc( region ); for( PaintClipper::Iterator iterator; !iterator.isDone(); iterator.next()) { XRenderColor col = { 0, 0, 0, 0xffff }; // black XRenderFillRectangle( display(), PictOpSrc, buffer, &col, 0, 0, displayWidth(), displayHeight()); } } void SceneXrender::windowGeometryShapeChanged( Toplevel* c ) { if( !windows.contains( c )) // this is ok, shape is not valid by default return; Window* w = windows[ c ]; w->discardPicture(); w->discardShape(); w->discardAlpha(); } void SceneXrender::windowOpacityChanged( Toplevel* c ) { if( !windows.contains( c )) // this is ok, alpha is created on demand return; Window* w = windows[ c ]; w->discardAlpha(); } void SceneXrender::windowClosed( Toplevel* c, Deleted* deleted ) { assert( windows.contains( c )); if( deleted != NULL ) { // replace c with deleted Window* w = windows.take( c ); w->updateToplevel( deleted ); windows[ deleted ] = w; } else { delete windows.take( c ); c->effectWindow()->setSceneWindow( NULL ); } } void SceneXrender::windowDeleted( Deleted* c ) { assert( windows.contains( c )); delete windows.take( c ); c->effectWindow()->setSceneWindow( NULL ); } void SceneXrender::windowAdded( Toplevel* c ) { assert( !windows.contains( c )); windows[ c ] = new Window( c ); c->effectWindow()->setSceneWindow( windows[ c ]); } //**************************************** // SceneXrender::Window //**************************************** QPixmap *SceneXrender::Window::temp_pixmap = 0; SceneXrender::Window::Window( Toplevel* c ) : Scene::Window( c ) , _picture( None ) , format( XRenderFindVisualFormat( display(), c->visual())) , alpha( None ) , alpha_cached_opacity(0.0) { } SceneXrender::Window::~Window() { discardPicture(); discardAlpha(); discardShape(); } // Create XRender picture for the pixmap with the window contents. Picture SceneXrender::Window::picture() { if( !toplevel->damage().isEmpty() && _picture != None ) { XRenderFreePicture( display(), _picture ); _picture = None; } if( _picture == None && format != NULL ) { // Get the pixmap with the window contents. Pixmap pix = toplevel->windowPixmap(); if( pix == None ) return None; _picture = XRenderCreatePicture( display(), pix, format, 0, 0 ); toplevel->resetDamage( toplevel->rect()); } return _picture; } void SceneXrender::Window::discardPicture() { if( _picture != None ) XRenderFreePicture( display(), _picture ); _picture = None; } void SceneXrender::Window::discardAlpha() { if( alpha != None ) XRenderFreePicture( display(), alpha ); alpha = None; } // Create XRender picture for the alpha mask. Picture SceneXrender::Window::alphaMask( double opacity ) { if( isOpaque() && qFuzzyCompare( opacity, 1.0 ) ) return None; bool created = false; if( alpha == None ) { // Create a 1x1 8bpp pixmap containing the given opacity in the alpha channel. Pixmap pixmap = XCreatePixmap( display(), rootWindow(), 1, 1, 8 ); XRenderPictFormat* format = XRenderFindStandardFormat( display(), PictStandardA8 ); XRenderPictureAttributes pa; pa.repeat = True; alpha = XRenderCreatePicture( display(), pixmap, format, CPRepeat, &pa ); XFreePixmap( display(), pixmap ); created = true; } if( created || !qFuzzyCompare( alpha_cached_opacity + 1.0, opacity + 1.0 ) ) { XRenderColor col; col.alpha = int( opacity * 0xffff ); XRenderFillRectangle( display(), PictOpSrc, alpha, &col, 0, 0, 1, 1 ); alpha_cached_opacity = opacity; } return alpha; } // Maps window coordinates to screen coordinates QRect SceneXrender::Window::mapToScreen( int mask, const WindowPaintData &data, const QRect &rect ) const { QRect r = rect; if( mask & PAINT_WINDOW_TRANSFORMED ) { // Apply the window transformation r.moveTo( r.x() * data.xScale + data.xTranslate, r.y() * data.yScale + data.yTranslate ); r.setWidth( r.width() * data.xScale ); r.setHeight( r.height() * data.yScale ); } // Move the rectangle to the screen position r.translate( x(), y() ); if( mask & PAINT_SCREEN_TRANSFORMED ) { // Apply the screen transformation r.moveTo( r.x() * screen_paint.xScale + screen_paint.xTranslate, r.y() * screen_paint.yScale + screen_paint.yTranslate ); r.setWidth( r.width() * screen_paint.xScale ); r.setHeight( r.height() * screen_paint.yScale ); } return r; } // Maps window coordinates to screen coordinates QPoint SceneXrender::Window::mapToScreen( int mask, const WindowPaintData &data, const QPoint &point ) const { QPoint pt = point; if( mask & PAINT_WINDOW_TRANSFORMED ) { // Apply the window transformation pt.rx() = pt.x() * data.xScale + data.xTranslate; pt.ry() = pt.y() * data.yScale + data.yTranslate; } // Move the point to the screen position pt += QPoint(x(), y()); if( mask & PAINT_SCREEN_TRANSFORMED ) { // Apply the screen transformation pt.rx() = pt.x() * screen_paint.xScale + screen_paint.xTranslate; pt.ry() = pt.y() * screen_paint.yScale + screen_paint.yTranslate; } return pt; } void SceneXrender::Window::prepareTempPixmap(const QPixmap *left, const QPixmap *top, const QPixmap *right, const QPixmap *bottom) { const QRect r = static_cast( toplevel )->decorationRect(); if( !temp_pixmap ) temp_pixmap = new QPixmap( r.width(), r.height() ); else if( temp_pixmap->width() < r.width() || temp_pixmap->height() < r.height() ) *temp_pixmap = QPixmap( r.width(), r.height() ); temp_pixmap->fill( Qt::transparent ); Display *dpy = display(); XRenderComposite( dpy, PictOpSrc, top->x11PictureHandle(), None, temp_pixmap->x11PictureHandle(), 0, 0, 0, 0, 0, 0, top->width(), top->height() ); XRenderComposite( dpy, PictOpSrc, left->x11PictureHandle(), None, temp_pixmap->x11PictureHandle(), 0, 0, 0, 0, 0, top->height(), left->width(), left->height() ); XRenderComposite( dpy, PictOpSrc, right->x11PictureHandle(), None, temp_pixmap->x11PictureHandle(), 0, 0, 0, 0, r.width() - right->width(), top->height(), right->width(), right->height() ); XRenderComposite( dpy, PictOpSrc, bottom->x11PictureHandle(), None, temp_pixmap->x11PictureHandle(), 0, 0, 0, 0, 0, r.height() - bottom->height(), bottom->width(), bottom->height() ); } // paint the window void SceneXrender::Window::performPaint( int mask, QRegion region, WindowPaintData data ) { setTransformedShape( QRegion()); // maybe nothing will be painted // check if there is something to paint bool opaque = isOpaque() && qFuzzyCompare( data.opacity, 1.0 ); /* HACK: It seems this causes painting glitches, disable temporarily if(( mask & PAINT_WINDOW_OPAQUE ) ^ ( mask & PAINT_WINDOW_TRANSLUCENT )) { // We are only painting either opaque OR translucent windows, not both if( mask & PAINT_WINDOW_OPAQUE && !opaque ) return; // Only painting opaque and window is translucent if( mask & PAINT_WINDOW_TRANSLUCENT && opaque ) return; // Only painting translucent and window is opaque }*/ Picture pic = picture(); // get XRender picture if( pic == None ) // The render format can be null for GL and/or Xv visuals return; // set picture filter if( options->xrenderSmoothScale ) // only when forced, it's slow { if( mask & PAINT_WINDOW_TRANSFORMED ) filter = ImageFilterGood; else if( mask & PAINT_SCREEN_TRANSFORMED ) filter = ImageFilterGood; else filter = ImageFilterFast; } else filter = ImageFilterFast; // do required transformations const QRect wr = mapToScreen(mask, data, QRect(0, 0, width(), height())); const QRect cr = QRect(toplevel->clientPos(), toplevel->clientSize()); // Client rect (in the window) const QRect dr = mapToScreen(mask, data, cr); // Destination rect double xscale = 1; double yscale = 1; bool scaled = false; Client *client = dynamic_cast( toplevel ); Deleted *deleted = dynamic_cast( toplevel ); if ( client && Workspace::self()->decorationHasAlpha() ) transformed_shape = QRegion( client->decorationRect() ); else if( deleted && Workspace::self()->decorationHasAlpha() ) transformed_shape = QRegion( deleted->decorationRect() ); else transformed_shape = shape(); XTransform xform = {{ { XDoubleToFixed( 1 ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) }, { XDoubleToFixed( 0 ), XDoubleToFixed( 1), XDoubleToFixed( 0 ) }, { XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) } }}; XTransform identity = {{ { XDoubleToFixed( 1 ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) }, { XDoubleToFixed( 0 ), XDoubleToFixed( 1), XDoubleToFixed( 0 ) }, { XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) } }}; XRenderPictureAttributes attr; if( mask & PAINT_WINDOW_TRANSFORMED ) { xscale = data.xScale; yscale = data.yScale; } if( mask & PAINT_SCREEN_TRANSFORMED ) { xscale *= screen_paint.xScale; yscale *= screen_paint.yScale; } if( !qFuzzyCompare( xscale, 1.0 ) || !qFuzzyCompare( yscale, 1.0 ) ) { scaled = true; xform.matrix[0][0] = XDoubleToFixed(1.0 / xscale); xform.matrix[1][1] = XDoubleToFixed(1.0 / yscale); XRenderSetPictureTransform( display(), pic, &xform ); if( filter == ImageFilterGood ) XRenderSetPictureFilter( display(), pic, const_cast< char* >( "good" ), NULL, 0 ); // This is needed to avoid hitting a fallback in the radeon driver. // The Render specification states that sampling pixels outside the // source picture results in alpha=0 pixels. This can be achieved by // setting the border color to transparent black, but since the border // color has the same format as the texture, it only works when the // texture has an alpha channel. So the driver falls back to software // when the repeat mode is RepeatNone, the picture has a non-identity // transformation matrix, and doesn't have an alpha channel. // Since we only scale the picture, we can work around this by setting // the repeat mode to RepeatPad. if (!window()->hasAlpha()) { attr.repeat = RepeatPad; XRenderChangePicture( display(), pic, CPRepeat, &attr ); } // transform the shape for clipping in paintTransformedScreen() QVector< QRect > rects = transformed_shape.rects(); for( int i = 0; i < rects.count(); ++i ) { QRect& r = rects[ i ]; r = QRect( int( r.x() * xscale ), int( r.y() * yscale ), int( r.width() * xscale ), int( r.height() * xscale )); } transformed_shape.setRects( rects.constData(), rects.count()); } transformed_shape.translate( mapToScreen( mask, data, QPoint(0, 0) ) ); PaintClipper pcreg( region ); // clip by the region to paint PaintClipper pc( transformed_shape ); // clip by window's shape for( PaintClipper::Iterator iterator; !iterator.isDone(); iterator.next()) { if( client || deleted ) { bool noBorder = true; const QPixmap *left = NULL; const QPixmap *top = NULL; const QPixmap *right = NULL; const QPixmap *bottom = NULL; QRect tr, lr, rr, br; QRect decorationRect; if( client && !client->noBorder() ) { noBorder = client->noBorder(); client->ensureDecorationPixmapsPainted(); left = client->leftDecoPixmap(); top = client->topDecoPixmap(); right = client->rightDecoPixmap(); bottom = client->bottomDecoPixmap(); client->layoutDecorationRects( lr, tr, rr, br, Client::WindowRelative ); decorationRect = client->decorationRect(); } if( deleted && !deleted->noBorder() ) { noBorder = deleted->noBorder(); left = deleted->leftDecoPixmap(); top = deleted->topDecoPixmap(); right = deleted->rightDecoPixmap(); bottom = deleted->bottomDecoPixmap(); deleted->layoutDecorationRects( lr, tr, rr, br ); decorationRect = deleted->decorationRect(); } if( !noBorder ) { // Paint the decoration Picture alpha = alphaMask( data.opacity * data.decoration_opacity ); Display *dpy = display(); if( !scaled ) { tr = mapToScreen( mask, data, tr ); lr = mapToScreen( mask, data, lr ); rr = mapToScreen( mask, data, rr ); br = mapToScreen( mask, data, br ); XRenderComposite( dpy, PictOpOver, top->x11PictureHandle(), alpha, buffer, 0, 0, 0, 0, tr.x(), tr.y(), tr.width(), tr.height()); XRenderComposite( dpy, PictOpOver, left->x11PictureHandle(), alpha, buffer, 0, 0, 0, 0, lr.x(), lr.y(), lr.width(), lr.height()); XRenderComposite( dpy, PictOpOver, right->x11PictureHandle(), alpha, buffer, 0, 0, 0, 0, rr.x(), rr.y(), rr.width(), rr.height()); XRenderComposite( dpy, PictOpOver, bottom->x11PictureHandle(), alpha, buffer, 0, 0, 0, 0, br.x(), br.y(), br.width(), br.height()); } else { const QRect r = mapToScreen( mask, data, decorationRect ); prepareTempPixmap( left, top, right, bottom ); XRenderSetPictureTransform( dpy, temp_pixmap->x11PictureHandle(), &xform ); XRenderComposite( dpy, PictOpOver, temp_pixmap->x11PictureHandle(), alpha, buffer, 0, 0, 0, 0, r.x(), r.y(), r.width(), r.height() ); XRenderSetPictureTransform( dpy, temp_pixmap->x11PictureHandle(), &identity ); } } } if ( !(mask & PAINT_DECORATION_ONLY) ) { // Paint the window contents if( opaque ) { XRenderComposite( display(), PictOpSrc, pic, None, buffer, cr.x() * xscale, cr.y() * yscale, 0, 0, dr.x(), dr.y(), dr.width(), dr.height()); } else { Picture alpha = alphaMask( data.opacity ); XRenderComposite( display(), PictOpOver, pic, alpha, buffer, cr.x() * xscale, cr.y() * yscale, 0, 0, dr.x(), dr.y(), dr.width(), dr.height()); transformed_shape = QRegion(); } } if( data.brightness < 1.0 ) { // fake brightness change by overlaying black XRenderColor col = { 0, 0, 0, 0xffff * ( 1 - data.brightness ) * data.opacity }; XRenderFillRectangle( display(), PictOpOver, buffer, &col, wr.x(), wr.y(), wr.width(), wr.height() ); } } if( scaled ) { XRenderSetPictureTransform( display(), pic, &identity ); if( filter == ImageFilterGood ) XRenderSetPictureFilter( display(), pic, const_cast< char* >( "fast" ), NULL, 0 ); if (!window()->hasAlpha()) { attr.repeat = RepeatNone; XRenderChangePicture( display(), pic, CPRepeat, &attr ); } } } //**************************************** // SceneXrender::EffectFrame //**************************************** SceneXrender::EffectFrame::EffectFrame( EffectFrameImpl* frame ) : Scene::EffectFrame( frame ) { m_picture = NULL; m_textPicture = NULL; m_iconPicture = NULL; m_selectionPicture = NULL; } SceneXrender::EffectFrame::~EffectFrame() { delete m_picture; delete m_textPicture; delete m_iconPicture; delete m_selectionPicture; } void SceneXrender::EffectFrame::free() { delete m_picture; m_picture = NULL; delete m_textPicture; m_textPicture = NULL; delete m_iconPicture; m_iconPicture = NULL; delete m_selectionPicture; m_selectionPicture = NULL; } void SceneXrender::EffectFrame::freeIconFrame() { delete m_iconPicture; m_iconPicture = NULL; } void SceneXrender::EffectFrame::freeTextFrame() { delete m_textPicture; m_textPicture = NULL; } void SceneXrender::EffectFrame::freeSelection() { delete m_selectionPicture; m_selectionPicture = NULL; } void SceneXrender::EffectFrame::crossFadeIcon() { // TODO: implement me } void SceneXrender::EffectFrame::crossFadeText() { // TODO: implement me } void SceneXrender::EffectFrame::render( QRegion region, double opacity, double frameOpacity ) { if( m_effectFrame->geometry().isEmpty() ) { return; // Nothing to display } // Render the actual frame if( m_effectFrame->style() == EffectFrameUnstyled ) xRenderRoundBox( effects->xrenderBufferPicture(), m_effectFrame->geometry().adjusted( -5, -5, 5, 5 ), 5, QColor( 0, 0, 0, int( opacity * frameOpacity * 255 ))); else if( m_effectFrame->style() == EffectFrameStyled ) { if( !m_picture ) // Lazy creation { updatePicture(); } qreal left, top, right, bottom; m_effectFrame->frame().getMargins( left, top, right, bottom ); // m_geometry is the inner geometry QRect geom = m_effectFrame->geometry().adjusted( -left, -top, right, bottom ); XRenderComposite( display(), PictOpOver, *m_picture, None, effects->xrenderBufferPicture(), 0, 0, 0, 0, geom.x(), geom.y(), geom.width(), geom.height() ); if( !m_effectFrame->selection().isNull() ) { if( !m_selectionPicture ) // Lazy creation { m_selectionPicture = new XRenderPicture( m_effectFrame->selectionFrame().framePixmap() ); } geom = m_effectFrame->selection(); XRenderComposite( display(), PictOpOver, *m_selectionPicture, None, effects->xrenderBufferPicture(), 0, 0, 0, 0, geom.x(), geom.y(), geom.width(), geom.height() ); } } XRenderPicture fill = xRenderBlendPicture(opacity); // Render icon if( !m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty() ) { QPoint topLeft( m_effectFrame->geometry().x(), m_effectFrame->geometry().center().y() - m_effectFrame->iconSize().height() / 2 ); if( !m_iconPicture ) // lazy creation m_iconPicture = new XRenderPicture( m_effectFrame->icon() ); QRect geom = QRect( topLeft, m_effectFrame->iconSize() ); XRenderComposite( display(), PictOpOver, *m_iconPicture, fill, effects->xrenderBufferPicture(), 0, 0, 0, 0, geom.x(), geom.y(), geom.width(), geom.height() ); } // Render text if( !m_effectFrame->text().isEmpty() ) { if( !m_textPicture ) // Lazy creation { updateTextPicture(); } XRenderComposite( display(), PictOpOver, *m_textPicture, fill, effects->xrenderBufferPicture(), 0, 0, 0, 0, m_effectFrame->geometry().x(), m_effectFrame->geometry().y(), m_effectFrame->geometry().width(), m_effectFrame->geometry().height() ); } } void SceneXrender::EffectFrame::updatePicture() { delete m_picture; if( m_effectFrame->style() == EffectFrameStyled ) m_picture = new XRenderPicture( m_effectFrame->frame().framePixmap() ); } void SceneXrender::EffectFrame::updateTextPicture() { // Mostly copied from SceneOpenGL::EffectFrame::updateTextTexture() above delete m_textPicture; if( m_effectFrame->text().isEmpty() ) { return; } // Determine position on texture to paint text QRect rect( QPoint( 0, 0 ), m_effectFrame->geometry().size() ); if( !m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty() ) { rect.setLeft( m_effectFrame->iconSize().width() ); } // If static size elide text as required QString text = m_effectFrame->text(); if( m_effectFrame->isStatic() ) { QFontMetrics metrics( m_effectFrame->text() ); text = metrics.elidedText( text, Qt::ElideRight, rect.width() ); } QPixmap pixmap( m_effectFrame->geometry().size() ); pixmap.fill( Qt::transparent ); QPainter p( &pixmap ); p.setFont( m_effectFrame->font() ); if( m_effectFrame->style() == EffectFrameStyled ) { p.setPen( m_effectFrame->styledTextColor() ); } else { // TODO: What about no frame? Custom color setting required p.setPen( Qt::white ); } p.drawText( rect, m_effectFrame->alignment(), text ); p.end(); m_textPicture = new XRenderPicture( pixmap ); } } // namespace #endif diff --git a/toplevel.cpp b/toplevel.cpp index bfa21486f..0454a5cb4 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -1,365 +1,365 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 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 "toplevel.h" #include #include "atoms.h" #include "client.h" #include "effects.h" namespace KWin { Toplevel::Toplevel( Workspace* ws ) : vis( NULL ) , info( NULL ) , ready_for_painting( true ) , client( None ) , frame( None ) , wspace( ws ) , window_pix( None ) #ifdef HAVE_XDAMAGE , damage_handle( None ) #endif , is_shape( false ) , effect_window( NULL ) , wmClientLeaderWin( 0 ) , unredirect( false ) , unredirectSuspend( false ) { } Toplevel::~Toplevel() { #ifdef HAVE_XDAMAGE assert( damage_handle == None ); #endif discardWindowPixmap(); delete info; } -kdbgstream& operator<<( kdbgstream& stream, const Toplevel* cl ) +QDebug& operator<<( QDebug& stream, const Toplevel* cl ) { if( cl == NULL ) return stream << "\'NULL\'"; cl->debug( stream ); return stream; } -kdbgstream& operator<<( kdbgstream& stream, const ToplevelList& list ) +QDebug& operator<<( QDebug& stream, const ToplevelList& list ) { stream << "LIST:("; bool first = true; for( ToplevelList::ConstIterator it = list.begin(); it != list.end(); ++it ) { if( !first ) stream << ":"; first = false; stream << *it; } stream << ")"; return stream; } -kdbgstream& operator<<( kdbgstream& stream, const ConstToplevelList& list ) +QDebug& operator<<( QDebug& stream, const ConstToplevelList& list ) { stream << "LIST:("; bool first = true; for( ConstToplevelList::ConstIterator it = list.begin(); it != list.end(); ++it ) { if( !first ) stream << ":"; first = false; stream << *it; } stream << ")"; return stream; } void Toplevel::detectShape( Window id ) { is_shape = Extensions::hasShape( id ); } // used only by Deleted::copy() void Toplevel::copyToDeleted( Toplevel* c ) { geom = c->geom; vis = c->vis; bit_depth = c->bit_depth; info = c->info; client = c->client; frame = c->frame; wspace = c->wspace; window_pix = c->window_pix; ready_for_painting = c->ready_for_painting; #ifdef HAVE_XDAMAGE damage_handle = None; #endif damage_region = c->damage_region; repaints_region = c->repaints_region; is_shape = c->is_shape; effect_window = c->effect_window; if( effect_window != NULL ) effect_window->setWindow( this ); resource_name = c->resourceName(); resource_class = c->resourceClass(); client_machine = c->wmClientMachine( false ); wmClientLeaderWin = c->wmClientLeader(); window_role = c->windowRole(); // this needs to be done already here, otherwise 'c' could very likely // call discardWindowPixmap() in something called during cleanup c->window_pix = None; } // before being deleted, remove references to everything that's now // owner by Deleted void Toplevel::disownDataPassedToDeleted() { info = NULL; } QRect Toplevel::visibleRect() const { return geometry(); } NET::WindowType Toplevel::windowType( bool direct, int supported_types ) const { if( supported_types == 0 ) supported_types = dynamic_cast< const Client* >( this ) != NULL ? SUPPORTED_MANAGED_WINDOW_TYPES_MASK : SUPPORTED_UNMANAGED_WINDOW_TYPES_MASK; NET::WindowType wt = info->windowType( supported_types ); if( direct ) return wt; const Client* cl = dynamic_cast< const Client* >( this ); NET::WindowType wt2 = cl ? cl->rules()->checkType( wt ) : wt; if( wt != wt2 ) { wt = wt2; info->setWindowType( wt ); // force hint change } // hacks here if( wt == NET::Menu && cl != NULL ) { // ugly hack to support the times when NET::Menu meant NET::TopMenu // if it's as wide as the screen, not very high and has its upper-left // corner a bit above the screen's upper-left cornet, it's a topmenu if( x() == 0 && y() < 0 && y() > -10 && height() < 100 && abs( width() - workspace()->clientArea( FullArea, cl ).width()) < 10 ) wt = NET::TopMenu; } // TODO change this to rule const char* const oo_prefix = "openoffice.org"; // QByteArray has no startsWith() // oo_prefix is lowercase, because resourceClass() is forced to be lowercase if( qstrncmp( resourceClass(), oo_prefix, strlen( oo_prefix )) == 0 && wt == NET::Dialog ) wt = NET::Normal; // see bug #66065 if( wt == NET::Unknown && cl != NULL ) // this is more or less suggested in NETWM spec wt = cl->isTransient() ? NET::Dialog : NET::Normal; return wt; } void Toplevel::getWindowRole() { window_role = getStringProperty( window(), atoms->wm_window_role).toLower(); } /*! Returns SM_CLIENT_ID property for a given window. */ QByteArray Toplevel::staticSessionId(WId w) { return getStringProperty(w, atoms->sm_client_id); } /*! Returns WM_COMMAND property for a given window. */ QByteArray Toplevel::staticWmCommand(WId w) { return getStringProperty(w, XA_WM_COMMAND, ' '); } /*! Returns WM_CLIENT_LEADER property for a given window. */ Window Toplevel::staticWmClientLeader(WId w) { Atom type; int format, status; unsigned long nitems = 0; unsigned long extra = 0; unsigned char *data = 0; Window result = w; KXErrorHandler err; status = XGetWindowProperty( display(), w, atoms->wm_client_leader, 0, 10000, false, XA_WINDOW, &type, &format, &nitems, &extra, &data ); if (status == Success && !err.error( false )) { if (data && nitems > 0) result = *((Window*) data); XFree(data); } return result; } void Toplevel::getWmClientLeader() { wmClientLeaderWin = staticWmClientLeader(window()); } /*! Returns sessionId for this client, taken either from its window or from the leader window. */ QByteArray Toplevel::sessionId() { QByteArray result = staticSessionId(window()); if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) result = staticSessionId(wmClientLeaderWin); return result; } /*! Returns command property for this client, taken either from its window or from the leader window. */ QByteArray Toplevel::wmCommand() { QByteArray result = staticWmCommand(window()); if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) result = staticWmCommand(wmClientLeaderWin); return result; } void Toplevel::getWmClientMachine() { client_machine = getStringProperty(window(), XA_WM_CLIENT_MACHINE); if( client_machine.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window()) client_machine = getStringProperty(wmClientLeaderWin, XA_WM_CLIENT_MACHINE); if( client_machine.isEmpty()) client_machine = "localhost"; } /*! Returns client machine for this client, taken either from its window or from the leader window. */ QByteArray Toplevel::wmClientMachine( bool use_localhost ) const { QByteArray result = client_machine; if( use_localhost ) { // special name for the local machine (localhost) if( result != "localhost" && isLocalMachine( result )) result = "localhost"; } return result; } /*! Returns client leader window for this client. Returns the client window itself if no leader window is defined. */ Window Toplevel::wmClientLeader() const { if (wmClientLeaderWin) return wmClientLeaderWin; return window(); } void Toplevel::getResourceClass() { XClassHint classHint; if( XGetClassHint( display(), window(), &classHint ) ) { // Qt3.2 and older had this all lowercase, Qt3.3 capitalized resource class. // Force lowercase, so that workarounds listing resource classes still work. resource_name = QByteArray( classHint.res_name ).toLower(); resource_class = QByteArray( classHint.res_class ).toLower(); XFree( classHint.res_name ); XFree( classHint.res_class ); } else { resource_name = resource_class = QByteArray(); } } double Toplevel::opacity() const { if( info->opacity() == 0xffffffff ) return 1.0; return info->opacity() * 1.0 / 0xffffffff; } void Toplevel::setOpacity( double new_opacity ) { double old_opacity = opacity(); new_opacity = qBound( 0.0, new_opacity, 1.0 ); if( old_opacity == new_opacity ) return; info->setOpacity( static_cast< unsigned long >( new_opacity * 0xffffffff )); if( compositing()) { addRepaintFull(); scene->windowOpacityChanged( this ); if( effects ) static_cast(effects)->windowOpacityChanged( effectWindow(), old_opacity ); } } void Toplevel::deleteEffectWindow() { delete effect_window; effect_window = NULL; } int Toplevel::screen() const { if( !options->xineramaEnabled ) return 0; int s = workspace()->screenNumber( geometry().center()); if( s < 0 ) { kDebug(1212) << "Invalid screen: Center" << geometry().center() << ", screen" << s; return 0; } return s; } bool Toplevel::isOnScreen( int screen ) const { if( !options->xineramaEnabled ) return screen == 0; return workspace()->screenGeometry( screen ).intersects( geometry()); } } // namespace #include "toplevel.moc" diff --git a/toplevel.h b/toplevel.h index 6ec12100d..54a4c0d11 100644 --- a/toplevel.h +++ b/toplevel.h @@ -1,454 +1,454 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 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_TOPLEVEL_H #define KWIN_TOPLEVEL_H #include #include #include #include #include #include "kactivityconsumer.h" #include "utils.h" #include "workspace.h" #ifdef HAVE_XDAMAGE #include #endif class NETWinInfo2; namespace KWin { class Workspace; class EffectWindowImpl; class Toplevel : public QObject, public KDecorationDefines { Q_OBJECT public: Toplevel( Workspace *ws ); Window frameId() const; Window window() const; Workspace* workspace() const; QRect geometry() const; QSize size() const; QPoint pos() const; QRect rect() const; int x() const; int y() const; int width() const; int height() const; bool isOnScreen( int screen ) const; // true if it's at least partially there int screen() const; // the screen where the center is virtual QPoint clientPos() const = 0; // inside of geometry() virtual QSize clientSize() const = 0; virtual QRect visibleRect() const; // the area the window occupies on the screen virtual QRect decorationRect() const; // rect including the decoration shadows virtual QRect transparentRect() const = 0; virtual QRegion decorationPendingRegion() const; // decoration region that needs to be repainted // prefer isXXX() instead // 0 for supported types means default for managed/unmanaged types NET::WindowType windowType( bool direct = false, int supported_types = 0 ) const; bool hasNETSupport() const; bool isDesktop() const; bool isDock() const; bool isToolbar() const; bool isTopMenu() const; bool isMenu() const; bool isNormalWindow() const; // normal as in 'NET::Normal or NET::Unknown non-transient' bool isDialog() const; bool isSplash() const; bool isUtility() const; bool isDropdownMenu() const; bool isPopupMenu() const; // a context popup, not dropdown, not torn-off bool isTooltip() const; bool isNotification() const; bool isComboBox() const; bool isDNDIcon() const; virtual int desktop() const = 0; virtual QStringList activities() const = 0; bool isOnDesktop( int d ) const; bool isOnActivity( const QString &activity ) const; bool isOnCurrentDesktop() const; bool isOnCurrentActivity() const; bool isOnAllDesktops() const; bool isOnAllActivities() const; QByteArray windowRole() const; QByteArray sessionId(); QByteArray resourceName() const; QByteArray resourceClass() const; QByteArray wmCommand(); QByteArray wmClientMachine( bool use_localhost ) const; Window wmClientLeader() const; pid_t pid() const; static bool resourceMatch( const Toplevel* c1, const Toplevel* c2 ); Pixmap windowPixmap( bool allow_create = true ); // may return None (e.g. at a bad moment while resizing) bool readyForPainting() const; // true if the window has been already painted its contents Visual* visual() const; bool shape() const; void setOpacity( double opacity ); double opacity() const; int depth() const; bool hasAlpha() const; virtual void setupCompositing(); virtual void finishCompositing(); bool updateUnredirectedState(); bool unredirected() const; void suspendUnredirect( bool suspend ); void addRepaint( const QRect& r ); void addRepaint( int x, int y, int w, int h ); virtual void addRepaintFull(); // these call workspace->addRepaint(), but first transform the damage if needed void addWorkspaceRepaint( const QRect& r ); void addWorkspaceRepaint( int x, int y, int w, int h ); QRegion repaints() const; void resetRepaints( const QRect& r ); QRegion damage() const; void resetDamage( const QRect& r ); EffectWindowImpl* effectWindow(); protected: virtual ~Toplevel(); void setWindowHandles( Window client, Window frame ); void detectShape( Window id ); virtual void propertyNotifyEvent( XPropertyEvent* e ); #ifdef HAVE_XDAMAGE virtual void damageNotifyEvent( XDamageNotifyEvent* e ); #endif Pixmap createWindowPixmap(); void discardWindowPixmap(); void addDamage( const QRect& r ); void addDamage( int x, int y, int w, int h ); void addDamageFull(); void getWmClientLeader(); void getWmClientMachine(); void getResourceClass(); void getWindowRole(); - virtual void debug( kdbgstream& stream ) const = 0; + virtual void debug( QDebug& stream ) const = 0; void copyToDeleted( Toplevel* c ); void disownDataPassedToDeleted(); - friend kdbgstream& operator<<( kdbgstream& stream, const Toplevel* ); + friend QDebug& operator<<( QDebug& stream, const Toplevel* ); void deleteEffectWindow(); virtual bool shouldUnredirect() const = 0; QRect geom; Visual* vis; int bit_depth; NETWinInfo2* info; bool ready_for_painting; QRegion repaints_region; // updating, repaint just requires repaint of that area private: static QByteArray staticWindowRole(WId); static QByteArray staticSessionId(WId); static QByteArray staticWmCommand(WId); static QByteArray staticWmClientMachine(WId); static Window staticWmClientLeader(WId); // when adding new data members, check also copyToDeleted() Window client; Window frame; Workspace* wspace; Pixmap window_pix; #ifdef HAVE_XDAMAGE Damage damage_handle; #endif QRegion damage_region; // damage is really damaged window (XDamage) and texture needs bool is_shape; EffectWindowImpl* effect_window; QByteArray resource_name; QByteArray resource_class; QByteArray client_machine; WId wmClientLeaderWin; QByteArray window_role; bool unredirect; bool unredirectSuspend; // when unredirected, but pixmap is needed temporarily // when adding new data members, check also copyToDeleted() }; inline Window Toplevel::window() const { return client; } inline Window Toplevel::frameId() const { return frame; } inline void Toplevel::setWindowHandles( Window w, Window f ) { assert( client == None && w != None ); client = w; assert( frame == None && f != None ); frame = f; } inline Workspace* Toplevel::workspace() const { return wspace; } inline QRect Toplevel::geometry() const { return geom; } inline QSize Toplevel::size() const { return geom.size(); } inline QPoint Toplevel::pos() const { return geom.topLeft(); } inline int Toplevel::x() const { return geom.x(); } inline int Toplevel::y() const { return geom.y(); } inline int Toplevel::width() const { return geom.width(); } inline int Toplevel::height() const { return geom.height(); } inline QRect Toplevel::rect() const { return QRect( 0, 0, width(), height()); } inline QRect Toplevel::decorationRect() const { return rect(); } inline QRegion Toplevel::decorationPendingRegion() const { return QRegion(); } inline bool Toplevel::readyForPainting() const { return ready_for_painting; } inline Visual* Toplevel::visual() const { return vis; } inline bool Toplevel::isDesktop() const { return windowType() == NET::Desktop; } inline bool Toplevel::isDock() const { return windowType() == NET::Dock; } inline bool Toplevel::isTopMenu() const { return windowType() == NET::TopMenu; } inline bool Toplevel::isMenu() const { return windowType() == NET::Menu && !isTopMenu(); // because of backwards comp. } inline bool Toplevel::isToolbar() const { return windowType() == NET::Toolbar; } inline bool Toplevel::isSplash() const { return windowType() == NET::Splash; } inline bool Toplevel::isUtility() const { return windowType() == NET::Utility; } inline bool Toplevel::isDialog() const { return windowType() == NET::Dialog; } inline bool Toplevel::isNormalWindow() const { return windowType() == NET::Normal; } inline bool Toplevel::isDropdownMenu() const { return windowType() == NET::DropdownMenu; } inline bool Toplevel::isPopupMenu() const { return windowType() == NET::PopupMenu; } inline bool Toplevel::isTooltip() const { return windowType() == NET::Tooltip; } inline bool Toplevel::isNotification() const { return windowType() == NET::Notification; } inline bool Toplevel::isComboBox() const { return windowType() == NET::ComboBox; } inline bool Toplevel::isDNDIcon() const { return windowType() == NET::DNDIcon; } inline Pixmap Toplevel::windowPixmap( bool allow_create ) { if( window_pix == None && allow_create ) window_pix = createWindowPixmap(); return window_pix; } inline QRegion Toplevel::damage() const { return damage_region; } inline QRegion Toplevel::repaints() const { return repaints_region; } inline bool Toplevel::shape() const { return is_shape; } inline int Toplevel::depth() const { return bit_depth; } inline bool Toplevel::hasAlpha() const { return depth() == 32; } inline EffectWindowImpl* Toplevel::effectWindow() { return effect_window; } inline bool Toplevel::isOnAllDesktops() const { return desktop() == NET::OnAllDesktops; } inline bool Toplevel::isOnAllActivities() const { return activities().isEmpty(); } inline bool Toplevel::isOnDesktop( int d ) const { return desktop() == d || /*desk == 0 ||*/ isOnAllDesktops(); } inline bool Toplevel::isOnActivity( const QString &activity ) const { return activities().isEmpty() || activities().contains(activity); } inline bool Toplevel::isOnCurrentDesktop() const { return isOnDesktop( workspace()->currentDesktop()); } inline bool Toplevel::isOnCurrentActivity() const { return isOnActivity( Workspace::self()->currentActivity()); } inline QByteArray Toplevel::resourceName() const { return resource_name; // it is always lowercase } inline QByteArray Toplevel::resourceClass() const { return resource_class; // it is always lowercase } inline QByteArray Toplevel::windowRole() const { return window_role; } inline pid_t Toplevel::pid() const { return info->pid(); } inline bool Toplevel::unredirected() const { return unredirect; } -kdbgstream& operator<<( kdbgstream& stream, const Toplevel* ); -kdbgstream& operator<<( kdbgstream& stream, const ToplevelList& ); -kdbgstream& operator<<( kdbgstream& stream, const ConstToplevelList& ); +QDebug& operator<<( QDebug& stream, const Toplevel* ); +QDebug& operator<<( QDebug& stream, const ToplevelList& ); +QDebug& operator<<( QDebug& stream, const ConstToplevelList& ); KWIN_COMPARE_PREDICATE( WindowMatchPredicate, Toplevel, Window, cl->window() == value ); KWIN_COMPARE_PREDICATE( FrameIdMatchPredicate, Toplevel, Window, cl->frameId() == value ); } // namespace #endif diff --git a/unmanaged.cpp b/unmanaged.cpp index 5c13cf0a6..fabd2fd59 100644 --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -1,142 +1,142 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 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 "unmanaged.h" #include "workspace.h" #include "effects.h" #include "deleted.h" #include namespace KWin { Unmanaged::Unmanaged( Workspace* ws ) : Toplevel( ws ) { } Unmanaged::~Unmanaged() { } bool Unmanaged::track( Window w ) { XWindowAttributes attr; grabXServer(); if( !XGetWindowAttributes(display(), w, &attr) || attr.map_state != IsViewable ) { ungrabXServer(); return false; } if( attr.c_class == InputOnly ) { ungrabXServer(); return false; } setWindowHandles( w, w ); // the window is also the frame XSelectInput( display(), w, attr.your_event_mask | StructureNotifyMask | PropertyChangeMask); geom = QRect( attr.x, attr.y, attr.width, attr.height ); vis = attr.visual; bit_depth = attr.depth; unsigned long properties[ 2 ]; properties[ NETWinInfo::PROTOCOLS ] = NET::WMWindowType | NET::WMPid | 0; properties[ NETWinInfo::PROTOCOLS2 ] = NET::WM2Opacity | 0; info = new NETWinInfo2( display(), w, rootWindow(), properties, 2 ); getResourceClass(); getWindowRole(); getWmClientLeader(); getWmClientMachine(); if( Extensions::shapeAvailable()) XShapeSelectInput( display(), w, ShapeNotifyMask ); detectShape( w ); setupCompositing(); ungrabXServer(); if( effects ) static_cast(effects)->checkInputWindowStacking(); return true; } void Unmanaged::release() { Deleted* del = Deleted::create( this ); if( effects ) { static_cast(effects)->windowClosed( effectWindow()); scene->windowClosed( this, del ); } finishCompositing(); workspace()->removeUnmanaged( this, Allowed ); if( !QWidget::find( window())) // don't affect our own windows { if( Extensions::shapeAvailable()) XShapeSelectInput( display(), window(), NoEventMask ); XSelectInput( display(), window(), NoEventMask ); } addWorkspaceRepaint( geometry()); disownDataPassedToDeleted(); del->unrefWindow(); deleteUnmanaged( this, Allowed ); } void Unmanaged::deleteUnmanaged( Unmanaged* c, allowed_t ) { delete c; } int Unmanaged::desktop() const { return NET::OnAllDesktops; // TODO for some window types should be the current desktop? } QStringList Unmanaged::activities() const { return QStringList(); } QPoint Unmanaged::clientPos() const { return QPoint( 0, 0 ); // unmanaged windows don't have decorations } QSize Unmanaged::clientSize() const { return size(); } QRect Unmanaged::transparentRect() const { return QRect(clientPos(), clientSize()); } -void Unmanaged::debug( kdbgstream& stream ) const +void Unmanaged::debug( QDebug& stream ) const { stream << "\'ID:" << window() << "\'"; } } // namespace #include "unmanaged.moc" diff --git a/unmanaged.h b/unmanaged.h index e92a6afd9..6d0ac1c48 100644 --- a/unmanaged.h +++ b/unmanaged.h @@ -1,59 +1,59 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 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_UNMANAGED_H #define KWIN_UNMANAGED_H #include #include "toplevel.h" namespace KWin { class Unmanaged : public Toplevel { Q_OBJECT public: Unmanaged( Workspace *ws ); bool windowEvent( XEvent* e ); void release(); bool track( Window w ); static void deleteUnmanaged( Unmanaged* c, allowed_t ); virtual int desktop() const; virtual QStringList activities() const; virtual QPoint clientPos() const; virtual QSize clientSize() const; virtual QRect transparentRect() const; protected: - virtual void debug( kdbgstream& stream ) const; + virtual void debug( QDebug& stream ) const; virtual bool shouldUnredirect() const; private: virtual ~Unmanaged(); // use release() // handlers for X11 events void mapNotifyEvent( XMapEvent* e ); void unmapNotifyEvent( XUnmapEvent*e ); void configureNotifyEvent( XConfigureEvent* e ); }; } // namespace #endif