<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -318,12 +318,8 @@ void Client::updateDecoration( bool check_workspace_pos, bool force )
         XReparentWindow( display(), decoration-&gt;widget()-&gt;winId(), frameId(), 0, 0 );
         decoration-&gt;widget()-&gt;lower();
         decoration-&gt;borders( border_left, border_right, border_top, border_bottom );
-        int save_workarea_diff_x = workarea_diff_x;
-        int save_workarea_diff_y = workarea_diff_y;
         move( calculateGravitation( false ));
         plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
-        workarea_diff_x = save_workarea_diff_x;
-        workarea_diff_y = save_workarea_diff_y;
         do_show = true;
         if( compositing() )
             discardWindowPixmap();
@@ -352,12 +348,8 @@ void Client::destroyDecoration()
         QPoint grav = calculateGravitation( true );
         border_left = border_right = border_top = border_bottom = 0;
         setMask( QRegion()); // Reset shape mask
-        int save_workarea_diff_x = workarea_diff_x;
-        int save_workarea_diff_y = workarea_diff_y;
         plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
         move( grav );
-        workarea_diff_x = save_workarea_diff_x;
-        workarea_diff_y = save_workarea_diff_y;
         if( compositing() )
             discardWindowPixmap();
         if( scene != NULL &amp;&amp; !deleting ) </diff>
      <filename>client.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -280,6 +280,8 @@ class Client
         void cancelShadeHoverTimer();
         void cancelAutoRaise();
         void checkActiveModal();
+        StrutRect strutRect( StrutArea area ) const;
+        StrutRects strutRects() const;
         bool hasStrut() const;
         /**
          * Whether or not the window has a strut that expands through the invisible area of
@@ -378,9 +380,7 @@ class Client
         void finishWindowRules();
         void setShortcutInternal( const KShortcut&amp; cut );
 
-        void updateWorkareaDiffs();
         void checkDirection( int new_diff, int old_diff, QRect&amp; rect, const QRect&amp; area );
-        static int computeWorkareaDiff( int left, int right, int a_left, int a_right );
         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 );
@@ -510,7 +510,6 @@ class Client
         QRect geom_restore;
         QRect geom_fs_restore;
         MaximizeMode maxmode_restore;
-        int workarea_diff_x, workarea_diff_y;
         QTimer* autoRaiseTimer;
         QTimer* shadeHoverTimer;
         QTimer* delayedMoveResizeTimer;</diff>
      <filename>client.h</filename>
    </modified>
    <modified>
      <diff>@@ -4,6 +4,7 @@
 
 Copyright (C) 1999, 2000 Matthias Ettrich &lt;ettrich@kde.org&gt;
 Copyright (C) 2003 Lubos Lunak &lt;l.lunak@kde.org&gt;
+Copyright (C) 2009 Lucas Murray &lt;lmurray@undefinedfire.com&gt;
 
 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
@@ -85,6 +86,7 @@ void Workspace::updateClientArea( bool force )
     int nscreens = Kephal::ScreenUtils::numScreens();
     kDebug(1212) &lt;&lt; &quot;screens: &quot; &lt;&lt; nscreens &lt;&lt; &quot;desktops: &quot; &lt;&lt; numberOfDesktops();
     QVector&lt; QRect &gt; new_wareas( numberOfDesktops() + 1 );
+    QVector&lt; StrutRects &gt; new_rmoveareas( numberOfDesktops() + 1 );
     QVector&lt; QVector&lt; QRect &gt; &gt; new_sareas( numberOfDesktops() + 1 );
     QVector&lt; QRect &gt; screens( nscreens );
     QRect desktopArea = Kephal::ScreenUtils::desktopGeometry();
@@ -110,6 +112,7 @@ void Workspace::updateClientArea( bool force )
         if( !(*it)-&gt;hasStrut())
             continue;
         QRect r = (*it)-&gt;adjustedClientArea( desktopArea, desktopArea );
+        StrutRects strutRegion = (*it)-&gt;strutRects();
 
         // Ignore offscreen xinerama struts. These interfere with the larger monitors on the setup
         // and should be ignored so that applications that use the work area to work out where
@@ -127,6 +130,7 @@ void Workspace::updateClientArea( bool force )
                 {
                 if( !hasOffscreenXineramaStrut )
                     new_wareas[ i ] = new_wareas[ i ].intersected( r );
+                new_rmoveareas[ i ] += strutRegion;
                 for( int iS = 0;
                      iS &lt; nscreens;
                      iS ++ )
@@ -140,6 +144,7 @@ void Workspace::updateClientArea( bool force )
             {
             if( !hasOffscreenXineramaStrut )
                 new_wareas[ (*it)-&gt;desktop() ] = new_wareas[ (*it)-&gt;desktop() ].intersected( r );
+            new_rmoveareas[ (*it)-&gt;desktop() ] += strutRegion;
             for( int iS = 0;
                  iS &lt; nscreens;
                  iS ++ )
@@ -170,7 +175,10 @@ void Workspace::updateClientArea( bool force )
         for( int i = 1;
              i &lt;= numberOfDesktops();
              ++i )
+            {
             new_wareas[ i ] = new_wareas[ i ].intersected( topmenu_area );
+            new_rmoveareas[ i ] += topmenu_area;
+            }
         }
 
     bool changed = force;
@@ -184,6 +192,8 @@ void Workspace::updateClientArea( bool force )
         {
         if( workarea[ i ] != new_wareas[ i ] )
             changed = true;
+        if( restrictedmovearea[ i ] != new_rmoveareas[ i ] )
+            changed = true;
         if( screenarea[ i ].size() != new_sareas[ i ].size())
             changed = true;
         for( int iS = 0;
@@ -196,6 +206,8 @@ void Workspace::updateClientArea( bool force )
     if ( changed )
         {
         workarea = new_wareas;
+        oldrestrictedmovearea = restrictedmovearea;
+        restrictedmovearea = new_rmoveareas;
         screenarea = new_sareas;
         NETRect r;
         for( int i = 1; i &lt;= numberOfDesktops(); i++)
@@ -296,6 +308,27 @@ QRect Workspace::clientArea( clientAreaOption opt, const Client* c ) const
     return clientArea( opt, c-&gt;geometry().center(), c-&gt;desktop());
     }
 
+QRegion Workspace::restrictedMoveArea( int desktop, StrutAreas areas ) const
+    {
+    if( desktop == NETWinInfo::OnAllDesktops || desktop == 0 )
+        desktop = currentDesktop();
+    QRegion region;
+    foreach( const StrutRect&amp; rect, restrictedmovearea[desktop] )
+        if( areas &amp; rect.area() )
+            region += rect;
+    return region;
+    }
+
+QRegion Workspace::previousRestrictedMoveArea( int desktop, StrutAreas areas ) const
+    {
+    if( desktop == NETWinInfo::OnAllDesktops || desktop == 0 )
+        desktop = currentDesktop();
+    QRegion region;
+    foreach( const StrutRect&amp; rect, oldrestrictedmovearea[desktop] )
+        if( areas &amp; rect.area() )
+            region += rect;
+    return region;
+    }
 
 /*!
   Client \a c is moved around to position \a pos. This gives the
@@ -964,6 +997,56 @@ NETExtendedStrut Client::strut() const
     return ext;
     }
 
+StrutRect Client::strutRect( StrutArea area ) const
+    {
+    assert( area != StrutAreaAll ); // Not valid
+    NETExtendedStrut strutArea = strut();
+    switch( area )
+        {
+        case StrutAreaTop:
+            if( strutArea.top_width != 0 )
+                return StrutRect( QRect(
+                    strutArea.top_start, 0,
+                    strutArea.top_end - strutArea.top_start, strutArea.top_width
+                    ), StrutAreaTop );
+            break;
+        case StrutAreaRight:
+            if( strutArea.right_width != 0 )
+                return StrutRect( QRect(
+                    displayWidth() - strutArea.right_width, strutArea.right_start,
+                    strutArea.right_width, strutArea.right_end - strutArea.right_start
+                    ), StrutAreaRight );
+            break;
+        case StrutAreaBottom:
+            if( strutArea.bottom_width != 0 )
+                return StrutRect( QRect(
+                    strutArea.bottom_start, displayHeight() - strutArea.bottom_width,
+                    strutArea.bottom_end - strutArea.bottom_start, strutArea.bottom_width
+                    ), StrutAreaBottom );
+            break;
+        case StrutAreaLeft:
+            if( strutArea.left_width != 0 )
+                return StrutRect( QRect(
+                    0, strutArea.left_start,
+                    strutArea.left_width, strutArea.left_end - strutArea.left_start
+                    ), StrutAreaLeft );
+            break;
+        default:
+            abort(); // Not valid
+        }
+    return StrutRect(); // Null rect
+    }
+
+StrutRects Client::strutRects() const
+    {
+    StrutRects region;
+    region += strutRect( StrutAreaTop );
+    region += strutRect( StrutAreaRight );
+    region += strutRect( StrutAreaBottom );
+    region += strutRect( StrutAreaLeft );
+    return region;
+    }
+
 bool Client::hasStrut() const
     {
     NETExtendedStrut ext = strut();
@@ -974,72 +1057,20 @@ bool Client::hasStrut() const
 
 bool Client::hasOffscreenXineramaStrut() const
     {
-    // Convert strut to a QRegion
-    NETExtendedStrut strutArea = strut();
-    QRegion strutRegion;
-    if( strutArea.left_width != 0 )
-        strutRegion += QRect(
-            0, strutArea.left_start,
-            strutArea.left_width, strutArea.left_end - strutArea.left_start
-            );
-    if( strutArea.right_width != 0 )
-        strutRegion += QRect(
-            displayWidth() - strutArea.right_width, strutArea.right_start,
-            strutArea.right_width, strutArea.right_end - strutArea.right_start
-            );
-    if( strutArea.top_width != 0 )
-        strutRegion += QRect(
-            strutArea.top_start, 0,
-            strutArea.top_end - strutArea.top_start, strutArea.top_width
-            );
-    if( strutArea.bottom_width != 0 )
-        strutRegion += QRect(
-            strutArea.bottom_start, displayHeight() - strutArea.bottom_width,
-            strutArea.bottom_end - strutArea.bottom_start, strutArea.bottom_width
-            );
+    // Get strut as a QRegion
+    QRegion region;
+    region += strutRect( StrutAreaTop );
+    region += strutRect( StrutAreaRight );
+    region += strutRect( StrutAreaBottom );
+    region += strutRect( StrutAreaLeft );
 
     // Remove all visible areas so that only the invisible remain
     int numScreens = Kephal::ScreenUtils::numScreens();
     for( int i = 0; i &lt; numScreens; i ++ )
-        strutRegion -= Kephal::ScreenUtils::screenGeometry( i );
+        region -= Kephal::ScreenUtils::screenGeometry( i );
 
     // If there's anything left then we have an offscreen strut
-    return !strutRegion.isEmpty();
-    }
-
-// updates differences to workarea edges for all directions
-void Client::updateWorkareaDiffs()
-    {
-    QRect area = workspace()-&gt;clientArea( WorkArea, this );
-    QRect geom = geometry();
-    workarea_diff_x = computeWorkareaDiff( geom.left(), geom.right(), area.left(), area.right());
-    workarea_diff_y = computeWorkareaDiff( geom.top(), geom.bottom(), area.top(), area.bottom());
-    }
-
-// If the client was inside workarea in the x direction, and if it was close to the left/right
-// edge, return the distance from the left/right edge (negative for left, positive for right)
-// INT_MIN means 'not inside workarea', INT_MAX means 'not near edge'.
-// In order to recognize 'at the left workarea edge' from 'at the right workarea edge'
-// (i.e. negative vs positive zero), the distances are one larger in absolute value than they
-// really are (i.e. 5 pixels from the left edge is -6, not -5). A bit hacky, but I'm lazy
-// to rewrite it just to make it nicer. If this will ever get touched again, perhaps then.
-// the y direction is done the same, just the values will be rotated: top-&gt;left, bottom-&gt;right
-int Client::computeWorkareaDiff( int left, int right, int a_left, int a_right )
-    {
-    int left_diff = left - a_left;
-    int right_diff = a_right - right;
-    if( left_diff &lt; 0 || right_diff &lt; 0 )
-        return INT_MIN;
-    else // fully inside workarea in this direction direction
-        {
-        // max distance from edge where it's still considered to be close and is kept at that distance
-        int max_diff = ( a_right - a_left ) / 10;
-        if( left_diff &lt; right_diff )
-            return left_diff &lt; max_diff ? -left_diff - 1 : INT_MAX;
-        else if( left_diff &gt; right_diff )
-            return right_diff &lt; max_diff ? right_diff + 1 : INT_MAX;
-        return INT_MAX; // not close to workarea edge
-        }
+    return !region.isEmpty();
     }
 
 void Client::checkWorkspacePosition()
@@ -1078,10 +1109,6 @@ void Client::checkWorkspacePosition()
 
     if( !isShade()) // TODO
         {
-        int old_diff_x = workarea_diff_x;
-        int old_diff_y = workarea_diff_y;
-        updateWorkareaDiffs();
-
         // this can be true only if this window was mapped before KWin
         // was started - in such case, don't adjust position to workarea,
         // because the window already had its position, and if a window
@@ -1090,27 +1117,125 @@ void Client::checkWorkspacePosition()
         if( workspace()-&gt;initializing())
             return;
 
-        QRect area = workspace()-&gt;clientArea( WorkArea, this );
-        QRect new_geom = geometry();
-        QRect tmp_rect_x( new_geom.left(), 0, new_geom.width(), 0 );
-        QRect tmp_area_x( area.left(), 0, area.width(), 0 );
-        checkDirection( workarea_diff_x, old_diff_x, tmp_rect_x, tmp_area_x );
-        // the x&lt;-&gt;y swapping
-        QRect tmp_rect_y( new_geom.top(), 0, new_geom.height(), 0 );
-        QRect tmp_area_y( area.top(), 0, area.height(), 0 );
-        checkDirection( workarea_diff_y, old_diff_y, tmp_rect_y, tmp_area_y );
-        new_geom = QRect( tmp_rect_x.left(), tmp_rect_y.left(), tmp_rect_x.width(), tmp_rect_y.width());
-        QRect final_geom( new_geom.topLeft(), adjustedSize( new_geom.size()));
-        if( final_geom != new_geom ) // size increments, or size restrictions
-            { // adjusted size differing matters only for right and bottom edge
-            if( old_diff_x != INT_MAX &amp;&amp; old_diff_x &gt; 0 )
-                final_geom.moveRight( area.right() - ( old_diff_x - 1 ));
-            if( old_diff_y != INT_MAX &amp;&amp; old_diff_y &gt; 0 )
-                final_geom.moveBottom( area.bottom() - ( old_diff_y - 1 ));
+        // If the window was touching an edge before but not now move it so it is again.
+        // Old and new maximums have different starting values so windows on the screen
+        // edge will move when a new strut is placed on the edge.
+        const QRect&amp; screenArea = workspace()-&gt;clientArea( ScreenArea, this );
+        int oldTopMax = screenArea.y();
+        int oldRightMax = screenArea.x() + screenArea.width();
+        int oldBottomMax = screenArea.y() + screenArea.height();
+        int oldLeftMax = screenArea.x();
+        int topMax = INT_MIN, rightMax = INT_MAX, bottomMax = INT_MAX, leftMax = INT_MIN;
+        QRect newGeom = geometry();
+        const QRect&amp; newGeomTall = QRect( newGeom.x(), 0, newGeom.width(), displayHeight() ); // Full screen height
+        const QRect&amp; newGeomWide = QRect( 0, newGeom.y(), displayWidth(), newGeom.height() ); // Full screen width
+
+        // Get the max strut point for each side where the window is (E.g. Highest point for
+        // the bottom struts bounded by the window's left and right sides).
+        foreach( QRect rect, workspace()-&gt;previousRestrictedMoveArea( desktop(), StrutAreaTop ).rects() )
+            {
+            rect &amp;= newGeomTall;
+            if( !rect.isEmpty() )
+                oldTopMax = qMax( oldTopMax, rect.y() + rect.height() );
+            }
+        foreach( QRect rect, workspace()-&gt;previousRestrictedMoveArea( desktop(), StrutAreaRight ).rects() )
+            {
+            rect &amp;= newGeomWide;
+            if( !rect.isEmpty() )
+                oldRightMax = qMin( oldRightMax, rect.x() );
+            }
+        foreach( QRect rect, workspace()-&gt;previousRestrictedMoveArea( desktop(), StrutAreaBottom ).rects() )
+            {
+            rect &amp;= newGeomTall;
+            if( !rect.isEmpty() )
+                oldBottomMax = qMin( oldBottomMax, rect.y() );
+            }
+        foreach( QRect rect, workspace()-&gt;previousRestrictedMoveArea( desktop(), StrutAreaLeft ).rects() )
+            {
+            rect &amp;= newGeomWide;
+            if( !rect.isEmpty() )
+                oldLeftMax = qMax( oldLeftMax, rect.x() + rect.width() );
+            }
+        foreach( QRect rect, workspace()-&gt;restrictedMoveArea( desktop(), StrutAreaTop ).rects() )
+            {
+            rect &amp;= newGeomTall;
+            if( !rect.isEmpty() )
+                topMax = qMax( topMax, rect.y() + rect.height() );
+            }
+        foreach( QRect rect, workspace()-&gt;restrictedMoveArea( desktop(), StrutAreaRight ).rects() )
+            {
+            rect &amp;= newGeomWide;
+            if( !rect.isEmpty() )
+                rightMax = qMin( rightMax, rect.x() );
             }
-        if( final_geom != geometry() )
-            setGeometry( final_geom );
-        //    updateWorkareaDiffs(); done already by setGeometry()
+        foreach( QRect rect, workspace()-&gt;restrictedMoveArea( desktop(), StrutAreaBottom ).rects() )
+            {
+            rect &amp;= newGeomTall;
+            if( !rect.isEmpty() )
+                bottomMax = qMin( bottomMax, rect.y() );
+            }
+        foreach( QRect rect, workspace()-&gt;restrictedMoveArea( desktop(), StrutAreaLeft ).rects() )
+            {
+            rect &amp;= newGeomWide;
+            if( !rect.isEmpty() )
+                leftMax = qMax( leftMax, rect.x() + rect.width() );
+            }
+
+        // Check if the sides were touching before but are no longer
+        if( newGeom.y() == oldTopMax &amp;&amp;
+            newGeom.y() != topMax )
+            { // Top was touching before but isn't anymore
+            // If the other side was touching make sure it still is afterwards
+            if( newGeom.y() + newGeom.height() == oldBottomMax )
+                newGeom.setTop( qMax( topMax, screenArea.y() ));
+            else
+                newGeom.moveTop( qMax( topMax, screenArea.y() ));
+            // Make sure it doesn't go off the other side of the screen/under an opposite strut
+            newGeom.setBottom( qMin( qMin( bottomMax - 1, screenArea.bottom() ),
+                newGeom.y() + newGeom.height() - 1 ));
+            }
+        if( newGeom.x() + newGeom.width() == oldRightMax &amp;&amp;
+            newGeom.x() + newGeom.width() != rightMax )
+            { // Right was touching before but isn't anymore
+            // If the other side was touching make sure it still is afterwards
+            if( newGeom.x() == oldLeftMax )
+                newGeom.setRight( qMin( rightMax - 1, screenArea.right() ));
+            else
+                newGeom.moveRight( qMin( rightMax - 1, screenArea.right() ));
+            // Make sure it doesn't go off the other side of the screen/under an opposite strut
+            newGeom.setLeft( qMax( qMax( leftMax, screenArea.x() ),
+                newGeom.x() ));
+            }
+        if( newGeom.y() + newGeom.height() == oldBottomMax &amp;&amp;
+            newGeom.y() + newGeom.height() != bottomMax )
+            { // Bottom was touching before but isn't anymore
+            // If the other side was touching make sure it still is afterwards
+            if( newGeom.y() == oldTopMax )
+                newGeom.setBottom( qMin( bottomMax - 1, screenArea.bottom() ));
+            else
+                newGeom.moveBottom( qMin( bottomMax - 1, screenArea.bottom() ));
+            // Make sure it doesn't go off the other side of the screen/under an opposite strut
+            newGeom.setTop( qMax( qMax( topMax, screenArea.y() ),
+                newGeom.y() ));
+            }
+        if( newGeom.x() == oldLeftMax &amp;&amp;
+            newGeom.x() != leftMax )
+            { // Left was touching before but isn't anymore
+            // If the other side was touching make sure it still is afterwards
+            if( newGeom.y() == newGeom.x() + newGeom.width() == oldRightMax )
+                newGeom.setLeft( qMax( leftMax, screenArea.x() ));
+            else
+                newGeom.moveLeft( qMax( leftMax, screenArea.x() ));
+            // Make sure it doesn't go off the other side of the screen/under an opposite strut
+            newGeom.setRight( qMin( qMin( rightMax - 1, screenArea.right() ),
+                newGeom.x() + newGeom.width() - 1 ));
+            }
+
+        // Obey size hints. TODO: We really should make sure it stays in the right place
+        newGeom.setSize( adjustedSize( newGeom.size() ));
+
+        if( newGeom != geometry() )
+            setGeometry( newGeom );
         }
     }
 
@@ -1710,24 +1835,6 @@ void Client::resizeWithChecks( int w, int h, ForceGeometry_t force )
             newy = newy + height() - h;
             break;
         }
-    // if it would be moved outside of workarea, keep it inside,
-    // see also Client::computeWorkareaDiff()
-    if( workarea_diff_x != INT_MIN &amp;&amp; w &lt;= area.width()) // was inside and can still fit
-        {
-        if( newx &lt; area.left())
-            newx = area.left();
-        if( newx + w &gt; area.right() + 1 )
-            newx = area.right() + 1 - w;
-        assert( newx &gt;= area.left() &amp;&amp; newx + w &lt;= area.right() + 1 ); // width was checked above
-        }
-    if( workarea_diff_y != INT_MIN &amp;&amp; h &lt;= area.height()) // was inside and can still fit
-        {
-        if( newy &lt; area.top())
-            newy = area.top();
-        if( newy + h &gt; area.bottom() + 1 )
-            newy = area.bottom() + 1 - h;
-        assert( newy &gt;= area.top() &amp;&amp; newy + h &lt;= area.bottom() + 1 ); // height was checked above
-        }
     setGeometry( newx, newy, w, h, force );
     }
 
@@ -1869,7 +1976,6 @@ void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force )
     if( force == NormalGeometrySet &amp;&amp; geom == g &amp;&amp; pending_geometry_update == PendingGeometryNone )
         return;
     geom = g;
-    updateWorkareaDiffs();
     if( block_geometry_updates != 0 )
         {
         if( pending_geometry_update == PendingGeometryForced )
@@ -1949,7 +2055,6 @@ void Client::plainResize( int w, int h, ForceGeometry_t force )
     if( force == NormalGeometrySet &amp;&amp; geom.size() == s )
         return;
     geom.setSize( s );
-    updateWorkareaDiffs();
     if( block_geometry_updates != 0 )
         {
         if( pending_geometry_update == PendingGeometryForced )
@@ -2003,7 +2108,6 @@ void Client::move( int x, int y, ForceGeometry_t force )
     if( force == NormalGeometrySet &amp;&amp; geom.topLeft() == p )
         return;
     geom.moveTopLeft( p );
-    updateWorkareaDiffs();
     if( block_geometry_updates != 0 )
         {
         if( pending_geometry_update == PendingGeometryForced )
@@ -2761,22 +2865,14 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root )
     if( workspace()-&gt;screenNumber( globalPos ) == -1 )
         return;
 
-    // compute bounds
-    // NOTE: This is duped in checkUnrestrictedMoveResize().
-    QRect desktopArea = workspace()-&gt;clientArea( WorkArea, globalPos, desktop());
-    int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
-    if( unrestrictedMoveResize ) // unrestricted, just don't let it go out completely
-        left_marge = right_marge = top_marge = bottom_marge = titlebar_marge = 5;
-    else // restricted move/resize - keep at least part of the titlebar always visible
-        {
-        // how much must remain visible when moved away in that direction
-        left_marge = qMin( 100 + border_right, moveResizeGeom.width());
-        right_marge = qMin( 100 + border_left, moveResizeGeom.width());
-        // width/height change with opaque resizing, use the initial ones
-        titlebar_marge = initialMoveResizeGeom.height();
-        top_marge = border_bottom;
-        bottom_marge = border_top;
-        }
+    // When doing a restricted move we must always keep 100px of the titlebar
+    // visible to allow the user to be able to move it again.
+    int frameLeft, frameRight, frameTop, frameBottom;
+    if( decoration )
+        decoration-&gt;borders( frameLeft, frameRight, frameTop, frameBottom );
+    else
+        frameTop = 10;
+    int titlebarArea = qMin( frameTop * 100, moveResizeGeom.width() * moveResizeGeom.height() );
 
     bool update = false;
     if( isResize())
@@ -2823,17 +2919,63 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root )
         // adjust new size to snap to other windows/borders
         moveResizeGeom = workspace()-&gt;adjustClientSize( this, moveResizeGeom, mode );
 
-        // NOTE: This is duped in checkUnrestrictedMoveResize().
-        if( moveResizeGeom.bottom() &lt; desktopArea.top() + top_marge )
-            moveResizeGeom.setBottom( desktopArea.top() + top_marge );
-        if( moveResizeGeom.top() &gt; desktopArea.bottom() - bottom_marge )
-            moveResizeGeom.setTop( desktopArea.bottom() - bottom_marge );
-        if( moveResizeGeom.right() &lt; desktopArea.left() + left_marge )
-            moveResizeGeom.setRight( desktopArea.left() + left_marge );
-        if( moveResizeGeom.left() &gt; desktopArea.right() - right_marge )
-            moveResizeGeom.setLeft(desktopArea.right() - right_marge );
-        if( !unrestrictedMoveResize &amp;&amp; moveResizeGeom.top() &lt; desktopArea.top() ) // titlebar mustn't go out
-            moveResizeGeom.setTop( desktopArea.top());
+        // Make sure the titlebar isn't behind a restricted area. We don't need to restrict
+        // the other directions. If not visible enough, move the window to the closest valid
+        // point. We bruteforce this by slowly moving the window back to its previous position.
+        for(;;)
+            {
+            QRegion titlebarRegion( moveResizeGeom.left(), moveResizeGeom.top(),
+                moveResizeGeom.width(), frameTop );
+            titlebarRegion &amp;= workspace()-&gt;clientArea( FullArea, -1, 0 ); // On the screen
+            titlebarRegion -= workspace()-&gt;restrictedMoveArea( desktop() ); // Strut areas
+            // Now we have a region of all the visible areas of the titlebar
+            // Count the visible pixels and check to see if it's enough
+            int visiblePixels = 0;
+            foreach( const QRect&amp; rect, titlebarRegion.rects() )
+                if( rect.height() &gt;= frameTop ) // Only the full height regions, prevents long slim areas
+                    visiblePixels += rect.width() * rect.height();
+            if( visiblePixels &gt;= titlebarArea )
+                break; // We have reached a valid position
+
+            // Not visible enough, move the window to the closest valid point. We bruteforce
+            // this by slowly moving the window back to its previous position.
+            if( previousMoveResizeGeom.y() != moveResizeGeom.y() )
+                {
+                if( previousMoveResizeGeom.y() &gt; moveResizeGeom.y() )
+                    moveResizeGeom.setTop( moveResizeGeom.y() + 1 );
+                else
+                    moveResizeGeom.setTop( moveResizeGeom.y() - 1 );
+                }
+            else  // Our heights match but we still don't have a valid area, maybe
+                { // we are trying to resize in from the side?
+                bool breakLoop = false;
+                switch( mode )
+                    {
+                    case PositionTopLeft:
+                    case PositionLeft:
+                        if( previousMoveResizeGeom.x() &gt;= moveResizeGeom.x() )
+                            {
+                            breakLoop = true;
+                            break;
+                            }
+                        moveResizeGeom.setLeft( moveResizeGeom.x() - 1 );
+                        break;
+                    case PositionTopRight:
+                    case PositionRight:
+                        if( previousMoveResizeGeom.right() &lt;= moveResizeGeom.right() )
+                            {
+                            breakLoop = true;
+                            break;
+                            }
+                        moveResizeGeom.setRight( moveResizeGeom.x() + moveResizeGeom.width() );
+                        break;
+                    default:
+                        breakLoop = true;
+                    }
+                if( breakLoop )
+                    break;
+                }
+            }
 
         QSize size = adjustedSize( moveResizeGeom.size(), sizemode );
         // the new topleft and bottomright corners (after checking size constrains), if they'll be needed
@@ -2891,16 +3033,33 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root )
             moveResizeGeom.moveTopLeft( topleft );
             moveResizeGeom.moveTopLeft( workspace()-&gt;adjustClientPosition( this, moveResizeGeom.topLeft(),
                                                                            unrestrictedMoveResize ));
-            // NOTE: This is duped in checkUnrestrictedMoveResize().
-            if( moveResizeGeom.bottom() &lt; desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
-                moveResizeGeom.moveBottom( desktopArea.top() + titlebar_marge - 1 );
-            // no need to check top_marge, titlebar_marge already handles it
-            if( moveResizeGeom.top() &gt; desktopArea.bottom() - bottom_marge )
-                moveResizeGeom.moveTop( desktopArea.bottom() - bottom_marge );
-            if( moveResizeGeom.right() &lt; desktopArea.left() + left_marge )
-                moveResizeGeom.moveRight( desktopArea.left() + left_marge );
-            if( moveResizeGeom.left() &gt; desktopArea.right() - right_marge )
-                moveResizeGeom.moveLeft(desktopArea.right() - right_marge );
+
+            // Make sure the titlebar isn't behind a restricted area.
+            for(;;)
+                {
+                QRegion titlebarRegion( moveResizeGeom.left(), moveResizeGeom.top(),
+                    moveResizeGeom.width(), frameTop );
+                titlebarRegion &amp;= workspace()-&gt;clientArea( FullArea, -1, 0 ); // On the screen
+                titlebarRegion -= workspace()-&gt;restrictedMoveArea( desktop() ); // Strut areas
+                // Now we have a region of all the visible areas of the titlebar
+                // Count the visible pixels and check to see if it's enough
+                int visiblePixels = 0;
+                foreach( const QRect&amp; rect, titlebarRegion.rects() )
+                    if( rect.height() &gt;= frameTop ) // Only the full height regions, prevents long slim areas
+                        visiblePixels += rect.width() * rect.height();
+                if( visiblePixels &gt;= titlebarArea )
+                    break; // We have reached a valid position
+
+                // Move it (Favour vertically)
+                if( previousMoveResizeGeom.y() != moveResizeGeom.y() )
+                    moveResizeGeom.translate( 0,
+                        previousMoveResizeGeom.y() &gt; moveResizeGeom.y() ? 1 : -1 );
+                else
+                    moveResizeGeom.translate( previousMoveResizeGeom.x() &gt; moveResizeGeom.x() ? 1 : -1,
+                        0 );
+                if( moveResizeGeom == previousMoveResizeGeom )
+                    break; // Prevent lockup
+                }
             }
         if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft())
             update = true;</diff>
      <filename>geometry.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -524,8 +524,6 @@ bool Client::manage( Window w, bool isMapped )
             user_time = xTime() - 1000000 + 10;
         }
 
-    updateWorkareaDiffs();
-
     //sendSyntheticConfigureNotify(); // Done when setting mapping state
 
     delete session;</diff>
      <filename>manage.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -57,6 +57,26 @@ namespace KWin
 
 #ifndef KCMRULES
 
+//************************************
+// StrutRect
+//************************************
+
+StrutRect::StrutRect( QRect rect, StrutArea area )
+    : QRect( rect )
+    , m_area( area )
+    {
+    }
+
+StrutRect::StrutRect( const StrutRect&amp; other )
+    : QRect( other )
+    , m_area( other.area() )
+    {
+    }
+
+//************************************
+// Motif
+//************************************
+
 void Motif::readFlags( WId w, bool&amp; noborder, bool&amp; resize, bool&amp; move,
     bool&amp; minimize, bool&amp; maximize, bool&amp; close )
     {</diff>
      <filename>utils.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -127,6 +127,29 @@ enum ActivityFlags
     ActivityRaise = 1 &lt;&lt; 2 // raise the window
     };
 
+enum StrutArea
+    {
+    StrutAreaInvalid = 0, // Null
+    StrutAreaTop     = 1 &lt;&lt; 0,
+    StrutAreaRight   = 1 &lt;&lt; 1,
+    StrutAreaBottom  = 1 &lt;&lt; 2,
+    StrutAreaLeft    = 1 &lt;&lt; 3,
+    StrutAreaAll     = StrutAreaTop | StrutAreaRight | StrutAreaBottom | StrutAreaLeft
+    };
+Q_DECLARE_FLAGS( StrutAreas, StrutArea )
+
+class StrutRect : public QRect
+    {
+    public:
+        StrutRect( QRect rect = QRect(), StrutArea area = StrutAreaInvalid );
+        StrutRect( const StrutRect&amp; other );
+        inline StrutArea area() const
+            { return m_area; };
+    private:
+        StrutArea m_area;
+    };
+typedef QVector&lt;StrutRect&gt; StrutRects;
+
 // Some KWin classes, mainly Client and Workspace, are very tighly coupled,
 // and some of the methods of one class may be called only from speficic places.
 // Those methods have additional allowed_t argument. If you pass Allowed
@@ -342,4 +365,7 @@ class ShortcutDialog
 
 } // namespace
 
+// Must be outside namespace
+Q_DECLARE_OPERATORS_FOR_FLAGS( KWin::StrutAreas )
+
 #endif</diff>
      <filename>utils.h</filename>
    </modified>
    <modified>
      <diff>@@ -1132,6 +1132,10 @@ void Workspace::loadDesktopSettings()
     desktopCount_ = n;
     workarea.clear();
     workarea.resize( n + 1 );
+    restrictedmovearea.clear();
+    restrictedmovearea.resize( n + 1 );
+    oldrestrictedmovearea.clear();
+    oldrestrictedmovearea.resize( n + 1 );
     screenarea.clear();
     rootInfo-&gt;setNumberOfDesktops( n );
     desktop_focus_chain.resize( n );</diff>
      <filename>workspace.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -90,6 +90,9 @@ class Workspace : public QObject, public KDecorationDefines
         QRect clientArea( clientAreaOption, const Client* c ) const;
         QRect clientArea( clientAreaOption, int screen, int desktop ) const;
 
+        QRegion restrictedMoveArea( int desktop, StrutAreas areas = StrutAreaAll ) const;
+        QRegion previousRestrictedMoveArea( int desktop, StrutAreas areas = StrutAreaAll ) const;
+
         /**
          * @internal
          */
@@ -853,6 +856,10 @@ class Workspace : public QObject, public KDecorationDefines
         Placement* initPositioning;
 
         QVector&lt;QRect&gt; workarea; // Array of workareas for virtual desktops
+        // Array of restricted areas that window cannot be moved into
+        QVector&lt;StrutRects&gt; restrictedmovearea;
+        // Array of the previous restricted areas that window cannot be moved into
+        QVector&lt;StrutRects&gt; oldrestrictedmovearea;
         QVector&lt; QVector&lt;QRect&gt; &gt; screenarea; // Array of workareas per xinerama screen for all virtual desktops
 
         bool managing_topmenus;</diff>
      <filename>workspace.h</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>6287be5fc2120017fcc2509e7ee69885853f2c98</id>
    </parent>
  </parents>
  <author>
    <name>Lucas Murray</name>
    <email>lmurray@undefinedfire.com</email>
  </author>
  <url>http://github.com/Zarin/kwin/commit/a0f0a03a75b4862e57883601731a13959b424b37</url>
  <id>a0f0a03a75b4862e57883601731a13959b424b37</id>
  <committed-date>2009-02-17T07:50:00-08:00</committed-date>
  <authored-date>2009-02-17T07:50:00-08:00</authored-date>
  <message>Improved window movement around struts. Windows can be moved anywhere
where the titlebar is still clickable even if it is outside the normal
work area. When struts are added or removed only move the windows that
cover the same area, leave all others untouched. If a strut is removed
on a xinerama screen that is not on the edge of the full desktop area
prevent the windows from being moved offscreen. Prevent struts/panels
from interfering with the movement of windows on other xinerama screens.
BUG: 74559
BUG: 90833
BUG: 160068

git-svn-id: svn+ssh://svn.kde.org/home/kde/trunk/KDE/kdebase/workspace/kwin@927466 283d02a7-25f6-0310-bc7c-ecb5cbfe19da</message>
  <tree>a24ec213da8b25960a5807bda30f5c6238773a0d</tree>
  <committer>
    <name>Lucas Murray</name>
    <email>lmurray@undefinedfire.com</email>
  </committer>
</commit>
