• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KWin

geometry.cpp

Go to the documentation of this file.
00001 /********************************************************************
00002  KWin - the KDE window manager
00003  This file is part of the KDE project.
00004 
00005 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
00006 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
00007 
00008 This program is free software; you can redistribute it and/or modify
00009 it under the terms of the GNU General Public License as published by
00010 the Free Software Foundation; either version 2 of the License, or
00011 (at your option) any later version.
00012 
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 GNU General Public License for more details.
00017 
00018 You should have received a copy of the GNU General Public License
00019 along with this program.  If not, see <http://www.gnu.org/licenses/>.
00020 *********************************************************************/
00021 
00022 /*
00023 
00024  This file contains things relevant to geometry, i.e. workspace size,
00025  window positions and window sizes.
00026 
00027 */
00028 
00029 #include "client.h"
00030 #include "workspace.h"
00031 
00032 #include <kapplication.h>
00033 #include <kglobal.h>
00034 #include <QPainter>
00035 #include <kwindowsystem.h>
00036 
00037 #include "placement.h"
00038 #include "notifications.h"
00039 #include "geometrytip.h"
00040 #include "rules.h"
00041 #include "effects.h"
00042 #include <QX11Info>
00043 
00044 #include <kephal/screens.h>
00045 
00046 namespace KWin
00047 {
00048 
00049 //********************************************
00050 // Workspace
00051 //********************************************
00052 
00056 void Workspace::desktopResized()
00057     {
00058     QRect geom = Kephal::ScreenUtils::desktopGeometry();
00059     NETSize desktop_geometry;
00060     desktop_geometry.width = geom.width();
00061     desktop_geometry.height = geom.height();
00062     rootInfo->setDesktopGeometry( -1, desktop_geometry );
00063 
00064     updateClientArea();
00065     destroyElectricBorders();
00066     updateElectricBorders();
00067     if( compositing() )
00068         {
00069         finishCompositing();
00070         QTimer::singleShot( 0, this, SLOT( setupCompositing() ) );
00071         }
00072     }
00073 
00086 void Workspace::updateClientArea( bool force )
00087     {
00088     int nscreens = Kephal::ScreenUtils::numScreens();
00089     kDebug(1212) << "screens: " << nscreens << "desktops: " << numberOfDesktops();
00090     QVector< QRect > new_wareas( numberOfDesktops() + 1 );
00091     QVector< QVector< QRect > > new_sareas( numberOfDesktops() + 1 );
00092     QVector< QRect > screens( nscreens );
00093     QRect desktopArea = Kephal::ScreenUtils::desktopGeometry();
00094     for( int iS = 0;
00095             iS < nscreens;
00096             iS ++ )
00097         {
00098         screens [iS] = Kephal::ScreenUtils::screenGeometry( iS );
00099         }
00100     for( int i = 1;
00101             i <= numberOfDesktops();
00102             ++i )
00103         {
00104         new_wareas[ i ] = desktopArea;
00105         new_sareas[ i ].resize( nscreens );
00106         for( int iS = 0;
00107              iS < nscreens;
00108              iS ++ )
00109             new_sareas[ i ][ iS ] = screens[ iS ];
00110         }
00111     for ( ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it)
00112         {
00113         if( !(*it)->hasStrut())
00114             continue;
00115         QRect r = (*it)->adjustedClientArea( desktopArea, desktopArea );
00116         if( (*it)->isOnAllDesktops())
00117             {
00118             for( int i = 1;
00119                  i <= numberOfDesktops();
00120                  ++i )
00121                 {
00122                 new_wareas[ i ] = new_wareas[ i ].intersected( r );
00123                 for( int iS = 0;
00124                      iS < nscreens;
00125                      iS ++ )
00126                     {
00127                     new_sareas[ i ][ iS ] = new_sareas[ i ][ iS ].intersected(
00128                         (*it)->adjustedClientArea( desktopArea, screens[ iS ] ));
00129                     }
00130                 }
00131             }
00132         else
00133             {
00134             new_wareas[ (*it)->desktop() ] = new_wareas[ (*it)->desktop() ].intersected( r );
00135             for( int iS = 0;
00136                  iS < nscreens;
00137                  iS ++ )
00138                 {
00139 //                            kDebug (1212) << "adjusting new_sarea: " << screens[ iS ];
00140                 new_sareas[ (*it)->desktop() ][ iS ]
00141                     = new_sareas[ (*it)->desktop() ][ iS ].intersected(
00142                         (*it)->adjustedClientArea( desktopArea, screens[ iS ] ));
00143                 }
00144             }
00145         }
00146 #if 0
00147     for( int i = 1;
00148             i <= numberOfDesktops();
00149             ++i )
00150         {
00151         for( int iS = 0;
00152              iS < nscreens;
00153              iS ++ )
00154             kDebug (1212) << "new_sarea: " << new_sareas[ i ][ iS ];
00155         }
00156 #endif
00157     // TODO topmenu update for screenarea changes?
00158     if( topmenu_space != NULL )
00159         {
00160         QRect topmenu_area = desktopArea;
00161         topmenu_area.setTop( topMenuHeight());
00162         for( int i = 1;
00163              i <= numberOfDesktops();
00164              ++i )
00165             new_wareas[ i ] = new_wareas[ i ].intersected( topmenu_area );
00166         }
00167 
00168     bool changed = force;
00169 
00170     if(screenarea.isEmpty())
00171         changed = true;
00172 
00173     for( int i = 1;
00174          !changed && i <= numberOfDesktops();
00175          ++i )
00176         {
00177         if( workarea[ i ] != new_wareas[ i ] )
00178             changed = true;
00179         if( screenarea[ i ].size() != new_sareas[ i ].size())
00180             changed = true;
00181         for( int iS = 0;
00182              !changed && iS < nscreens;
00183              iS ++ )
00184             if (new_sareas[ i ][ iS ] != screenarea [ i ][ iS ])
00185                 changed = true;
00186         }
00187 
00188     if ( changed )
00189         {
00190         workarea = new_wareas;
00191         screenarea = new_sareas;
00192         NETRect r;
00193         for( int i = 1; i <= numberOfDesktops(); i++)
00194             {
00195             r.pos.x = workarea[ i ].x();
00196             r.pos.y = workarea[ i ].y();
00197             r.size.width = workarea[ i ].width();
00198             r.size.height = workarea[ i ].height();
00199             rootInfo->setWorkArea( i, r );
00200             }
00201 
00202         updateTopMenuGeometry();
00203         for( ClientList::ConstIterator it = clients.constBegin();
00204              it != clients.constEnd();
00205              ++it)
00206             (*it)->checkWorkspacePosition();
00207         for( ClientList::ConstIterator it = desktops.constBegin();
00208              it != desktops.constEnd();
00209              ++it)
00210             (*it)->checkWorkspacePosition();
00211         }
00212         
00213     kDebug(1212) << "Done.";
00214     }
00215 
00216 void Workspace::updateClientArea()
00217     {
00218     updateClientArea( false );
00219     }
00220 
00221 
00229 QRect Workspace::clientArea( clientAreaOption opt, int screen, int desktop ) const
00230     {
00231     if( desktop == NETWinInfo::OnAllDesktops || desktop == 0 )
00232         desktop = currentDesktop();
00233     if( screen == -1 )
00234         screen = activeScreen();
00235     
00236     QRect sarea = (!screenarea.isEmpty() 
00237             && screen < screenarea[ desktop ].size()) // screens may be missing during KWin initialization or screen config changes
00238         ? screenarea[ desktop ][ screen ]
00239         : Kephal::ScreenUtils::screenGeometry( screen );
00240     QRect warea = workarea[ desktop ].isNull()
00241         ? Kephal::ScreenUtils::desktopGeometry()
00242         : workarea[ desktop ];
00243     switch (opt)
00244         {
00245         case MaximizeArea:
00246             if (options->xineramaMaximizeEnabled)
00247                 return sarea;
00248             else
00249                 return warea;
00250         case MaximizeFullArea:
00251             if (options->xineramaMaximizeEnabled)
00252                 return Kephal::ScreenUtils::screenGeometry( screen );
00253             else
00254                 return Kephal::ScreenUtils::desktopGeometry();
00255         case FullScreenArea:
00256             if (options->xineramaFullscreenEnabled)
00257                 return Kephal::ScreenUtils::screenGeometry( screen );
00258             else
00259                 return Kephal::ScreenUtils::desktopGeometry();
00260         case PlacementArea:
00261             if (options->xineramaPlacementEnabled)
00262                 return sarea;
00263             else
00264                 return warea;
00265         case MovementArea:
00266             if (options->xineramaMovementEnabled)
00267                 return Kephal::ScreenUtils::screenGeometry( screen );
00268             else
00269                 return Kephal::ScreenUtils::desktopGeometry();
00270         case WorkArea:
00271             return warea;
00272         case FullArea:
00273             return Kephal::ScreenUtils::desktopGeometry();
00274         case ScreenArea:
00275             return Kephal::ScreenUtils::screenGeometry( screen );
00276         }
00277     abort();
00278     }
00279 
00280 QRect Workspace::clientArea( clientAreaOption opt, const QPoint& p, int desktop ) const
00281     {
00282     int screen = Kephal::ScreenUtils::screenId( p );
00283     return clientArea( opt, screen, desktop );
00284     }
00285 
00286 QRect Workspace::clientArea( clientAreaOption opt, const Client* c ) const
00287     {
00288     return clientArea( opt, c->geometry().center(), c->desktop());
00289     }
00290 
00291 
00297 QPoint Workspace::adjustClientPosition( Client* c, QPoint pos, bool unrestricted )
00298     {
00299    //CT 16mar98, 27May98 - magics: BorderSnapZone, WindowSnapZone
00300    //CT adapted for kwin on 25Nov1999
00301    //aleXXX 02Nov2000 added second snapping mode
00302     if (options->windowSnapZone || options->borderSnapZone || options->centerSnapZone )
00303         {
00304         const bool sOWO=options->snapOnlyWhenOverlapping;
00305         const QRect maxRect = clientArea(MovementArea, pos+c->rect().center(), c->desktop());
00306         const int xmin = maxRect.left();
00307         const int xmax = maxRect.right()+1;               //desk size
00308         const int ymin = maxRect.top();
00309         const int ymax = maxRect.bottom()+1;
00310 
00311         const int cx(pos.x());
00312         const int cy(pos.y());
00313         const int cw(c->width());
00314         const int ch(c->height());
00315         const int rx(cx+cw);
00316         const int ry(cy+ch);                 //these don't change
00317 
00318         int nx(cx), ny(cy);                         //buffers
00319         int deltaX(xmax);
00320         int deltaY(ymax);   //minimum distance to other clients
00321 
00322         int lx, ly, lrx, lry; //coords and size for the comparison client, l
00323 
00324       // border snap
00325         int snap = options->borderSnapZone; //snap trigger
00326         if (snap)
00327             {
00328             if ((sOWO?(cx<xmin):true) && (qAbs(xmin-cx)<snap))
00329                 {
00330                 deltaX = xmin-cx;
00331                 nx = xmin;
00332                 }
00333             if ((sOWO?(rx>xmax):true) && (qAbs(rx-xmax)<snap) && (qAbs(xmax-rx) < deltaX))
00334                 {
00335                 deltaX = rx-xmax;
00336                 nx = xmax - cw;
00337                 }
00338 
00339             if ((sOWO?(cy<ymin):true) && (qAbs(ymin-cy)<snap))
00340                 {
00341                 deltaY = ymin-cy;
00342                 ny = ymin;
00343                 }
00344             if ((sOWO?(ry>ymax):true) && (qAbs(ry-ymax)<snap) && (qAbs(ymax-ry) < deltaY))
00345                 {
00346                 deltaY =ry-ymax;
00347                 ny = ymax - ch;
00348                 }
00349             }
00350 
00351       // windows snap
00352         snap = options->windowSnapZone;
00353         if (snap)
00354             {
00355             QList<Client *>::ConstIterator l;
00356             for (l = clients.constBegin();l != clients.constEnd();++l )
00357                 {
00358                 if ((*l)->isOnDesktop(currentDesktop()) &&
00359                    !(*l)->isMinimized()
00360                     && (*l) != c )
00361                     {
00362                     lx = (*l)->x();
00363                     ly = (*l)->y();
00364                     lrx = lx + (*l)->width();
00365                     lry = ly + (*l)->height();
00366 
00367                     if ( (( cy <= lry ) && ( cy  >= ly  ))  ||
00368                          (( ry >= ly  ) && ( ry  <= lry ))  ||
00369                          (( cy <= ly  ) && ( ry >= lry  )) )
00370                         {
00371                         if ((sOWO?(cx<lrx):true) && (qAbs(lrx-cx)<snap) && ( qAbs(lrx -cx) < deltaX) )
00372                             {
00373                             deltaX = qAbs( lrx - cx );
00374                             nx = lrx;
00375                             }
00376                         if ((sOWO?(rx>lx):true) && (qAbs(rx-lx)<snap) && ( qAbs( rx - lx )<deltaX) )
00377                             {
00378                             deltaX = qAbs(rx - lx);
00379                             nx = lx - cw;
00380                             }
00381                         }
00382 
00383                     if ( (( cx <= lrx ) && ( cx  >= lx  ))  ||
00384                          (( rx >= lx  ) && ( rx  <= lrx ))  ||
00385                          (( cx <= lx  ) && ( rx >= lrx  )) )
00386                         {
00387                         if ((sOWO?(cy<lry):true) && (qAbs(lry-cy)<snap) && (qAbs( lry -cy ) < deltaY))
00388                             {
00389                             deltaY = qAbs( lry - cy );
00390                             ny = lry;
00391                             }
00392                   //if ( (qAbs( ry-ly ) < snap) && (qAbs( ry - ly ) < deltaY ))
00393                         if ((sOWO?(ry>ly):true) && (qAbs(ry-ly)<snap) && (qAbs( ry - ly ) < deltaY ))
00394                             {
00395                             deltaY = qAbs( ry - ly );
00396                             ny = ly - ch;
00397                             }
00398                         }
00399 
00400                     // Corner snapping
00401                     if( nx == lrx || nx+cw == lx )
00402                         {
00403                         if ((sOWO?(ry>lry):true) && (qAbs(lry-ry)<snap) && (qAbs(lry-ry) < deltaY))
00404                             {
00405                             deltaY = qAbs( lry - ry );
00406                             ny = lry - ch;
00407                             }
00408                         if ((sOWO?(cy<ly):true) && (qAbs(cy-ly)<snap) && (qAbs(cy-ly) < deltaY))
00409                             {
00410                             deltaY = qAbs( cy - ly );
00411                             ny = ly;
00412                             }
00413                         }
00414                     if( ny == lry || ny+ch == ly )
00415                         {
00416                         if ((sOWO?(rx>lrx):true) && (qAbs(lrx-rx)<snap) && (qAbs(lrx-rx) < deltaX))
00417                             {
00418                             deltaX = qAbs( lrx - rx );
00419                             nx = lrx - cw;
00420                             }
00421                         if ((sOWO?(cx<lx):true) && (qAbs(cx-lx)<snap) && (qAbs(cx-lx) < deltaX))
00422                             {
00423                             deltaX = qAbs( cx - lx );
00424                             nx = lx;
00425                             }
00426                         }
00427                     }
00428                 }
00429             }
00430 
00431       // center snap
00432         snap = options->centerSnapZone; //snap trigger
00433         if (snap)
00434             {
00435             int diffX = qAbs( (xmin + xmax)/2 - (cx + cw/2) );
00436             int diffY = qAbs( (ymin + ymax)/2 - (cy + ch/2) );
00437             if (diffX < snap && diffY < snap && diffX < deltaX && diffY < deltaY)
00438                 { // Snap to center of screen
00439                 deltaX = diffX;
00440                 deltaY = diffY;
00441                 nx = (xmin + xmax)/2 - cw/2;
00442                 ny = (ymin + ymax)/2 - ch/2;
00443                 }
00444             else if ( options->borderSnapZone )
00445                 { // Enhance border snap
00446                 if( ( nx == xmin || nx == xmax - cw ) && diffY < snap && diffY < deltaY)
00447                     { // Snap to vertical center on screen edge
00448                     deltaY = diffY;
00449                     ny = (ymin + ymax)/2 - ch/2;
00450                     }
00451                 else if ( (( unrestricted ? ny == ymin : ny <= ymin) || ny == ymax - ch ) &&
00452                           diffX < snap && diffX < deltaX)
00453                     { // Snap to horizontal center on screen edge
00454                     deltaX = diffX;
00455                     nx = (xmin + xmax)/2 - cw/2;
00456                     }
00457                 }
00458             }
00459 
00460         pos = QPoint(nx, ny);
00461         }
00462     return pos;
00463     }
00464 
00465 QRect Workspace::adjustClientSize( Client* c, QRect moveResizeGeom, int mode )
00466     {
00467    //adapted from adjustClientPosition on 29May2004
00468    //this function is called when resizing a window and will modify
00469    //the new dimensions to snap to other windows/borders if appropriate
00470     if ( options->windowSnapZone || options->borderSnapZone ) // || options->centerSnapZone )
00471         {
00472         const bool sOWO=options->snapOnlyWhenOverlapping;
00473 
00474         const QRect maxRect = clientArea(MovementArea, c->rect().center(), c->desktop());
00475         const int xmin = maxRect.left();
00476         const int xmax = maxRect.right();               //desk size
00477         const int ymin = maxRect.top();
00478         const int ymax = maxRect.bottom();
00479 
00480         const int cx(moveResizeGeom.left());
00481         const int cy(moveResizeGeom.top());
00482         const int rx(moveResizeGeom.right());
00483         const int ry(moveResizeGeom.bottom());
00484 
00485         int newcx(cx), newcy(cy);                         //buffers
00486         int newrx(rx), newry(ry);
00487         int deltaX(xmax);
00488         int deltaY(ymax);   //minimum distance to other clients
00489 
00490         int lx, ly, lrx, lry; //coords and size for the comparison client, l
00491 
00492       // border snap
00493         int snap = options->borderSnapZone; //snap trigger
00494         if (snap)
00495             {
00496             deltaX = int(snap);
00497             deltaY = int(snap);
00498 
00499 #define SNAP_BORDER_TOP \
00500             if ((sOWO?(newcy<ymin):true) && (qAbs(ymin-newcy)<deltaY)) \
00501               { \
00502                 deltaY = qAbs(ymin-newcy); \
00503                 newcy = ymin; \
00504                }
00505 
00506 #define SNAP_BORDER_BOTTOM \
00507             if ((sOWO?(newry>ymax):true) && (qAbs(ymax-newry)<deltaY)) \
00508               { \
00509                 deltaY = qAbs(ymax-newcy); \
00510                 newry = ymax; \
00511                }
00512 
00513 #define SNAP_BORDER_LEFT \
00514             if ((sOWO?(newcx<xmin):true) && (qAbs(xmin-newcx)<deltaX)) \
00515               { \
00516                 deltaX = qAbs(xmin-newcx); \
00517                 newcx = xmin; \
00518                }
00519 
00520 #define SNAP_BORDER_RIGHT \
00521             if ((sOWO?(newrx>xmax):true) && (qAbs(xmax-newrx)<deltaX)) \
00522               { \
00523                 deltaX = qAbs(xmax-newrx); \
00524                 newrx = xmax; \
00525                }
00526                      switch ( mode )
00527                       {
00528                       case PositionBottomRight:
00529                         SNAP_BORDER_BOTTOM
00530                         SNAP_BORDER_RIGHT
00531                         break;
00532                       case PositionRight:
00533                         SNAP_BORDER_RIGHT
00534                         break;
00535                       case PositionBottom:
00536                         SNAP_BORDER_BOTTOM
00537                         break;
00538                       case PositionTopLeft:
00539                         SNAP_BORDER_TOP
00540                         SNAP_BORDER_LEFT
00541                         break;
00542                       case PositionLeft:
00543                         SNAP_BORDER_LEFT
00544                         break;
00545                       case PositionTop:
00546                         SNAP_BORDER_TOP
00547                         break;
00548                       case PositionTopRight:
00549                         SNAP_BORDER_TOP
00550                         SNAP_BORDER_RIGHT
00551                         break;
00552                       case PositionBottomLeft:
00553                         SNAP_BORDER_BOTTOM
00554                         SNAP_BORDER_LEFT
00555                         break;
00556                       default:
00557                         abort();
00558                         break;
00559                       }
00560 
00561 
00562             }
00563 
00564       // windows snap
00565         snap = options->windowSnapZone;
00566         if (snap)
00567             {
00568             deltaX = int(snap);
00569             deltaY = int(snap);
00570             QList<Client *>::ConstIterator l;
00571             for (l = clients.constBegin();l != clients.constEnd();++l )
00572                 {
00573                 if ((*l)->isOnDesktop(currentDesktop()) &&
00574                    !(*l)->isMinimized()
00575                     && (*l) != c )
00576                     {
00577                     lx = (*l)->x()-1;
00578                     ly = (*l)->y()-1;
00579                     lrx =(*l)->x() + (*l)->width();
00580                     lry =(*l)->y() + (*l)->height();
00581 
00582 #define WITHIN_HEIGHT ((( newcy <= lry ) && ( newcy  >= ly  ))  || \
00583                          (( newry >= ly  ) && ( newry  <= lry ))  || \
00584                          (( newcy <= ly  ) && ( newry >= lry  )) )
00585 
00586 #define WITHIN_WIDTH  ( (( cx <= lrx ) && ( cx  >= lx  ))  || \
00587                          (( rx >= lx  ) && ( rx  <= lrx ))  || \
00588                          (( cx <= lx  ) && ( rx >= lrx  )) )
00589 
00590 #define SNAP_WINDOW_TOP  if ( (sOWO?(newcy<lry):true) \
00591                   && WITHIN_WIDTH  \
00592                   && (qAbs( lry - newcy ) < deltaY) ) {  \
00593                   deltaY = qAbs( lry - newcy ); \
00594                   newcy=lry; \
00595                   }
00596 
00597 #define SNAP_WINDOW_BOTTOM  if ( (sOWO?(newry>ly):true)  \
00598                      && WITHIN_WIDTH  \
00599                      && (qAbs( ly - newry ) < deltaY) ) {  \
00600                      deltaY = qAbs( ly - newry );  \
00601                      newry=ly;  \
00602                      }
00603 
00604 #define SNAP_WINDOW_LEFT  if ( (sOWO?(newcx<lrx):true)  \
00605                    && WITHIN_HEIGHT  \
00606                    && (qAbs( lrx - newcx ) < deltaX)) {  \
00607                    deltaX = qAbs( lrx - newcx );  \
00608                    newcx=lrx;  \
00609                    }
00610 
00611 #define SNAP_WINDOW_RIGHT  if ( (sOWO?(newrx>lx):true)  \
00612                     && WITHIN_HEIGHT  \
00613                     && (qAbs( lx - newrx ) < deltaX))  \
00614                     {  \
00615                     deltaX = qAbs( lx - newrx );  \
00616                     newrx=lx;  \
00617                     }
00618 
00619 #define SNAP_WINDOW_C_TOP  if ( (sOWO?(newcy<ly):true)  \
00620                   && (newcx == lrx || newrx == lx)  \
00621                   && qAbs(ly-newcy) < deltaY ) {  \
00622                   deltaY = qAbs( ly - newcy + 1 ); \
00623                   newcy = ly + 1; \
00624                   }
00625 
00626 #define SNAP_WINDOW_C_BOTTOM  if ( (sOWO?(newry>lry):true)  \
00627                   && (newcx == lrx || newrx == lx)  \
00628                   && qAbs(lry-newry) < deltaY ) {  \
00629                   deltaY = qAbs( lry - newry - 1 ); \
00630                   newry = lry - 1; \
00631                   }
00632 
00633 #define SNAP_WINDOW_C_LEFT  if ( (sOWO?(newcx<lx):true)  \
00634                   && (newcy == lry || newry == ly)  \
00635                   && qAbs(lx-newcx) < deltaX ) {  \
00636                   deltaX = qAbs( lx - newcx + 1 ); \
00637                   newcx = lx + 1; \
00638                   }
00639 
00640 #define SNAP_WINDOW_C_RIGHT  if ( (sOWO?(newrx>lrx):true)  \
00641                   && (newcy == lry || newry == ly)  \
00642                   && qAbs(lrx-newrx) < deltaX ) {  \
00643                   deltaX = qAbs( lrx - newrx - 1 ); \
00644                   newrx = lrx - 1; \
00645                   }
00646 
00647                     switch ( mode )
00648                       {
00649                       case PositionBottomRight:
00650                         SNAP_WINDOW_BOTTOM
00651                         SNAP_WINDOW_RIGHT
00652                         SNAP_WINDOW_C_BOTTOM
00653                         SNAP_WINDOW_C_RIGHT
00654                         break;
00655                       case PositionRight:
00656                         SNAP_WINDOW_RIGHT
00657                         SNAP_WINDOW_C_RIGHT
00658                         break;
00659                       case PositionBottom:
00660                         SNAP_WINDOW_BOTTOM
00661                         SNAP_WINDOW_C_BOTTOM
00662                         break;
00663                       case PositionTopLeft:
00664                         SNAP_WINDOW_TOP
00665                         SNAP_WINDOW_LEFT
00666                         SNAP_WINDOW_C_TOP
00667                         SNAP_WINDOW_C_LEFT
00668                         break;
00669                       case PositionLeft:
00670                         SNAP_WINDOW_LEFT
00671                         SNAP_WINDOW_C_LEFT
00672                         break;
00673                       case PositionTop:
00674                         SNAP_WINDOW_TOP
00675                         SNAP_WINDOW_C_TOP
00676                         break;
00677                       case PositionTopRight:
00678                         SNAP_WINDOW_TOP
00679                         SNAP_WINDOW_RIGHT
00680                         SNAP_WINDOW_C_TOP
00681                         SNAP_WINDOW_C_RIGHT
00682                         break;
00683                       case PositionBottomLeft:
00684                         SNAP_WINDOW_BOTTOM
00685                         SNAP_WINDOW_LEFT
00686                         SNAP_WINDOW_C_BOTTOM
00687                         SNAP_WINDOW_C_LEFT
00688                         break;
00689                       default:
00690                         abort();
00691                         break;
00692                       }
00693                     }
00694                 }
00695             }
00696 
00697       // center snap
00698         //snap = options->centerSnapZone;
00699         //if (snap)
00700         //    {
00701         //    // Don't resize snap to center as it interferes too much
00702         //    // There are two ways of implementing this if wanted:
00703         //    // 1) Snap only to the same points that the move snap does, and
00704         //    // 2) Snap to the horizontal and vertical center lines of the screen
00705         //    }
00706 
00707        moveResizeGeom = QRect(QPoint(newcx, newcy), QPoint(newrx, newry));
00708        }
00709     return moveResizeGeom;
00710     }
00711 
00715 void Workspace::setClientIsMoving( Client *c )
00716     {
00717     Q_ASSERT(!c || !movingClient); // Catch attempts to move a second
00718     // window while still moving the first one.
00719     movingClient = c;
00720     if (movingClient)
00721         ++block_focus;
00722     else
00723         --block_focus;
00724     }
00725 
00729 void Workspace::cascadeDesktop()
00730     {
00731 // TODO XINERAMA this probably is not right for xinerama
00732     Q_ASSERT( block_stacking_updates == 0 );
00733     ClientList::ConstIterator it(stackingOrder().begin());
00734     initPositioning->reinitCascading( currentDesktop());
00735     QRect area = clientArea( PlacementArea, QPoint( 0, 0 ), currentDesktop());
00736     for (; it != stackingOrder().end(); ++it)
00737         {
00738         if((!(*it)->isOnDesktop(currentDesktop())) ||
00739            ((*it)->isMinimized())                  ||
00740            ((*it)->isOnAllDesktops())              ||
00741            (!(*it)->isMovable()) )
00742             continue;
00743         initPositioning->placeCascaded(*it, area);
00744         }
00745     }
00746 
00751 void Workspace::unclutterDesktop()
00752     {
00753     for ( int i = clients.size() - 1; i>=0; i-- )
00754         {
00755         if( ( !clients.at( i )->isOnDesktop( currentDesktop() ) ) ||
00756            (clients.at( i )->isMinimized())                  ||
00757            (clients.at( i )->isOnAllDesktops())              ||
00758            (!clients.at( i )->isMovable()) )
00759             continue;
00760         initPositioning->placeSmart(clients.at(  i ), QRect());
00761         }
00762     }
00763 
00764 
00765 void Workspace::updateTopMenuGeometry( Client* c )
00766     {
00767     if( !managingTopMenus())
00768         return;
00769     if( c != NULL )
00770         {
00771         XEvent ev;
00772         ev.xclient.display = display();
00773         ev.xclient.type = ClientMessage;
00774         ev.xclient.window = c->window();
00775         static Atom msg_type_atom = XInternAtom( display(), "_KDE_TOPMENU_MINSIZE", False );
00776         ev.xclient.message_type = msg_type_atom;
00777         ev.xclient.format = 32;
00778         ev.xclient.data.l[0] = xTime();
00779         ev.xclient.data.l[1] = topmenu_space->width();
00780         ev.xclient.data.l[2] = topmenu_space->height();
00781         ev.xclient.data.l[3] = 0;
00782         ev.xclient.data.l[4] = 0;
00783         XSendEvent( display(), c->window(), False, NoEventMask, &ev );
00784         KWindowSystem::setStrut( c->window(), 0, 0, topmenu_height, 0 ); // so that kicker etc. know
00785         c->checkWorkspacePosition();
00786         return;
00787         }
00788     // c == NULL - update all, including topmenu_space
00789     QRect area;
00790     area = clientArea( MaximizeFullArea, QPoint( 0, 0 ), 1 ); // HACK desktop ?
00791     area.setHeight( topMenuHeight());
00792     topmenu_space->setGeometry( area );
00793     for( ClientList::ConstIterator it = topmenus.constBegin();
00794          it != topmenus.constEnd();
00795          ++it )
00796         updateTopMenuGeometry( *it );
00797     }
00798 
00799 //********************************************
00800 // Client
00801 //********************************************
00802 
00803 
00804 void Client::keepInArea( QRect area, bool partial )
00805     {
00806     if( partial )
00807         {
00808         // increase the area so that can have only 100 pixels in the area
00809         area.setLeft( qMin( area.left() - width() + 100, area.left()));
00810         area.setTop( qMin( area.top() - height() + 100, area.top()));
00811         area.setRight( qMax( area.right() + width() - 100, area.right()));
00812         area.setBottom( qMax( area.bottom() + height() - 100, area.bottom()));
00813         }
00814     if( !partial )
00815         { // resize to fit into area
00816         if( area.width() < width() || area.height() < height())
00817             resizeWithChecks( qMin( area.width(), width()), qMin( area.height(), height()));
00818         }
00819     if ( geometry().right() > area.right() && width() < area.width() )
00820         move( area.right() - width(), y() );
00821     if ( geometry().bottom() > area.bottom() && height() < area.height() )
00822         move( x(), area.bottom() - height() );
00823     if( !area.contains( geometry().topLeft() ))
00824         {
00825         int tx = x();
00826         int ty = y();
00827         if ( tx < area.x() )
00828             tx = area.x();
00829         if ( ty < area.y() )
00830             ty = area.y();
00831         move( tx, ty );
00832         }
00833     }
00834 
00840 // TODO move to Workspace?
00841 
00842 QRect Client::adjustedClientArea( const QRect &desktopArea, const QRect& area ) const
00843     {
00844     QRect r = area;
00845     // topmenu area is reserved in updateClientArea()
00846     if( isTopMenu())
00847         return r;
00848     NETExtendedStrut str = strut();
00849     QRect stareaL = QRect(
00850             0,
00851             str . left_start,
00852             str . left_width,
00853             str . left_end - str . left_start + 1 );
00854     QRect stareaR = QRect (
00855             desktopArea . right () - str . right_width + 1,
00856             str . right_start,
00857             str . right_width,
00858             str . right_end - str . right_start + 1 );
00859     QRect stareaT = QRect (
00860             str . top_start,
00861             0,
00862             str . top_end - str . top_start + 1,
00863             str . top_width);
00864     QRect stareaB = QRect (
00865             str . bottom_start,
00866             desktopArea . bottom () - str . bottom_width + 1,
00867             str . bottom_end - str . bottom_start + 1,
00868             str . bottom_width);
00869 
00870     QRect screenarea = workspace()->clientArea( ScreenArea, this );
00871     // HACK: workarea handling is not xinerama aware, so if this strut
00872     // reserves place at a xinerama edge that's inside the virtual screen,
00873     // ignore the strut for workspace setting.
00874     if( area == Kephal::ScreenUtils::desktopGeometry())
00875         {
00876         if( stareaL.left() < screenarea.left())
00877             stareaL = QRect();
00878         if( stareaR.right() > screenarea.right())
00879             stareaR = QRect();
00880         if( stareaT.top() < screenarea.top())
00881             stareaT = QRect();
00882         if( stareaB.bottom() < screenarea.bottom())
00883             stareaB = QRect();
00884         }
00885     // Handle struts at xinerama edges that are inside the virtual screen.
00886     // They're given in virtual screen coordinates, make them affect only
00887     // their xinerama screen.
00888     stareaL.setLeft( qMax( stareaL.left(), screenarea.left()));
00889     stareaR.setRight( qMin( stareaR.right(), screenarea.right()));
00890     stareaT.setTop( qMax( stareaT.top(), screenarea.top()));
00891     stareaB.setBottom( qMin( stareaB.bottom(), screenarea.bottom()));
00892 
00893     if (stareaL . intersects (area)) {
00894 //        kDebug (1212) << "Moving left of: " << r << " to " << stareaL.right() + 1;
00895         r . setLeft( stareaL . right() + 1 );
00896     }
00897     if (stareaR . intersects (area)) {
00898 //        kDebug (1212) << "Moving right of: " << r << " to " << stareaR.left() - 1;
00899         r . setRight( stareaR . left() - 1 );
00900     }
00901     if (stareaT . intersects (area)) {
00902 //        kDebug (1212) << "Moving top of: " << r << " to " << stareaT.bottom() + 1;
00903         r . setTop( stareaT . bottom() + 1 );
00904     }
00905     if (stareaB . intersects (area)) {
00906 //        kDebug (1212) << "Moving bottom of: " << r << " to " << stareaB.top() - 1;
00907         r . setBottom( stareaB . top() - 1 );
00908     }
00909     return r;
00910     }
00911 
00912 NETExtendedStrut Client::strut() const
00913     {
00914     NETExtendedStrut ext = info->extendedStrut();
00915     NETStrut str = info->strut();
00916     if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0
00917         && ( str.left != 0 || str.right != 0 || str.top != 0 || str.bottom != 0 ))
00918         {
00919         // build extended from simple
00920         if( str.left != 0 )
00921             {
00922             ext.left_width = str.left;
00923             ext.left_start = 0;
00924             ext.left_end = displayHeight();
00925             }
00926         if( str.right != 0 )
00927             {
00928             ext.right_width = str.right;
00929             ext.right_start = 0;
00930             ext.right_end = displayHeight();
00931             }
00932         if( str.top != 0 )
00933             {
00934             ext.top_width = str.top;
00935             ext.top_start = 0;
00936             ext.top_end = displayWidth();
00937             }
00938         if( str.bottom != 0 )
00939             {
00940             ext.bottom_width = str.bottom;
00941             ext.bottom_start = 0;
00942             ext.bottom_end = displayWidth();
00943             }
00944         }
00945     return ext;
00946     }
00947 
00948 bool Client::hasStrut() const
00949     {
00950     NETExtendedStrut ext = strut();
00951     if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0 )
00952         return false;
00953     return true;
00954     }
00955 
00956 
00957 // updates differences to workarea edges for all directions
00958 void Client::updateWorkareaDiffs()
00959     {
00960     QRect area = workspace()->clientArea( WorkArea, this );
00961     QRect geom = geometry();
00962     workarea_diff_x = computeWorkareaDiff( geom.left(), geom.right(), area.left(), area.right());
00963     workarea_diff_y = computeWorkareaDiff( geom.top(), geom.bottom(), area.top(), area.bottom());
00964     }
00965 
00966 // If the client was inside workarea in the x direction, and if it was close to the left/right
00967 // edge, return the distance from the left/right edge (negative for left, positive for right)
00968 // INT_MIN means 'not inside workarea', INT_MAX means 'not near edge'.
00969 // In order to recognize 'at the left workarea edge' from 'at the right workarea edge'
00970 // (i.e. negative vs positive zero), the distances are one larger in absolute value than they
00971 // really are (i.e. 5 pixels from the left edge is -6, not -5). A bit hacky, but I'm lazy
00972 // to rewrite it just to make it nicer. If this will ever get touched again, perhaps then.
00973 // the y direction is done the same, just the values will be rotated: top->left, bottom->right
00974 int Client::computeWorkareaDiff( int left, int right, int a_left, int a_right )
00975     {
00976     int left_diff = left - a_left;
00977     int right_diff = a_right - right;
00978     if( left_diff < 0 || right_diff < 0 )
00979         return INT_MIN;
00980     else // fully inside workarea in this direction direction
00981         {
00982         // max distance from edge where it's still considered to be close and is kept at that distance
00983         int max_diff = ( a_right - a_left ) / 10;
00984         if( left_diff < right_diff )
00985             return left_diff < max_diff ? -left_diff - 1 : INT_MAX;
00986         else if( left_diff > right_diff )
00987             return right_diff < max_diff ? right_diff + 1 : INT_MAX;
00988         return INT_MAX; // not close to workarea edge
00989         }
00990     }
00991 
00992 void Client::checkWorkspacePosition()
00993     {
00994     if( isDesktop())
00995         return;
00996     if( isFullScreen())
00997         {
00998         QRect area = workspace()->clientArea( FullScreenArea, this );
00999         if( geometry() != area )
01000             setGeometry( area );
01001         return;
01002         }
01003     if( isDock())
01004         return;
01005     if( isTopMenu())
01006         {
01007         if( workspace()->managingTopMenus())
01008             {
01009             QRect area;
01010             ClientList mainclients = mainClients();
01011             if( mainclients.count() == 1 )
01012                 area = workspace()->clientArea( MaximizeFullArea, mainclients.first());
01013             else
01014                 area = workspace()->clientArea( MaximizeFullArea, QPoint( 0, 0 ), desktop());
01015             area.setHeight( workspace()->topMenuHeight());
01016 //            kDebug(1212) << "TOPMENU size adjust: " << area << ":" << this;
01017             setGeometry( area );
01018             }
01019         return;
01020         }
01021 
01022     if( maximizeMode() != MaximizeRestore )
01023     // TODO update geom_restore?
01024         changeMaximize( false, false, true ); // adjust size
01025 
01026     if( !isShade()) // TODO
01027         {
01028         int old_diff_x = workarea_diff_x;
01029         int old_diff_y = workarea_diff_y;
01030         updateWorkareaDiffs();
01031 
01032         // this can be true only if this window was mapped before KWin
01033         // was started - in such case, don't adjust position to workarea,
01034         // because the window already had its position, and if a window
01035         // with a strut altering the workarea would be managed in initialization
01036         // after this one, this window would be moved
01037         if( workspace()->initializing())
01038             return;
01039 
01040         QRect area = workspace()->clientArea( WorkArea, this );
01041         QRect new_geom = geometry();
01042         QRect tmp_rect_x( new_geom.left(), 0, new_geom.width(), 0 );
01043         QRect tmp_area_x( area.left(), 0, area.width(), 0 );
01044         checkDirection( workarea_diff_x, old_diff_x, tmp_rect_x, tmp_area_x );
01045         // the x<->y swapping
01046         QRect tmp_rect_y( new_geom.top(), 0, new_geom.height(), 0 );
01047         QRect tmp_area_y( area.top(), 0, area.height(), 0 );
01048         checkDirection( workarea_diff_y, old_diff_y, tmp_rect_y, tmp_area_y );
01049         new_geom = QRect( tmp_rect_x.left(), tmp_rect_y.left(), tmp_rect_x.width(), tmp_rect_y.width());
01050         QRect final_geom( new_geom.topLeft(), adjustedSize( new_geom.size()));
01051         if( final_geom != new_geom ) // size increments, or size restrictions
01052             { // adjusted size differing matters only for right and bottom edge
01053             if( old_diff_x != INT_MAX && old_diff_x > 0 )
01054                 final_geom.moveRight( area.right() - ( old_diff_x - 1 ));
01055             if( old_diff_y != INT_MAX && old_diff_y > 0 )
01056                 final_geom.moveBottom( area.bottom() - ( old_diff_y - 1 ));
01057             }
01058         if( final_geom != geometry() )
01059             setGeometry( final_geom );
01060         //    updateWorkareaDiffs(); done already by setGeometry()
01061         }
01062     }
01063 
01064 // Try to be smart about keeping the clients visible.
01065 // If the client was fully inside the workspace before, try to keep
01066 // it still inside the workarea, possibly moving it or making it smaller if possible,
01067 // and try to keep the distance from the nearest workarea edge.
01068 // On the other hand, it it was partially moved outside of the workspace in some direction,
01069 // don't do anything with that direction if it's still at least partially visible. If it's
01070 // not visible anymore at all, make sure it's visible at least partially
01071 // again (not fully, as that could(?) be potentionally annoying) by
01072 // moving it slightly inside the workarea (those '+ 5').
01073 // Again, this is done for the x direction, y direction will be done by x<->y swapping
01074 void Client::checkDirection( int new_diff, int old_diff, QRect& rect, const QRect& area )
01075     {
01076     if( old_diff != INT_MIN ) // was inside workarea
01077         {
01078         if( old_diff == INT_MAX ) // was in workarea, but far from edge
01079             {
01080             if( new_diff == INT_MIN )  // is not anymore fully in workarea
01081                 {
01082                 rect.setLeft( area.left());
01083                 rect.setRight( area.right());
01084                 }
01085             return;
01086             }
01087         if( isMovable())
01088             {
01089             if( old_diff < 0 ) // was in left third, keep distance from left edge
01090                 rect.moveLeft( area.left() + ( -old_diff - 1 ));
01091             else // old_diff > 0 // was in right third, keep distance from right edge
01092                 rect.moveRight( area.right() - ( old_diff - 1 ));
01093             }
01094         else if( isResizable())
01095             {
01096             if( old_diff < 0 )
01097                 rect.setLeft( area.left() + ( -old_diff - 1 ) );
01098             else // old_diff > 0
01099                 rect.setRight( area.right() - ( old_diff - 1 ));
01100             }
01101         if( rect.width() > area.width() && isResizable())
01102             rect.setWidth( area.width());
01103         if( isMovable())
01104             {
01105             if( rect.left() < area.left())
01106                 rect.moveLeft( area.left());
01107             else if( rect.right() > area.right())
01108                 rect.moveRight( area.right());
01109             }
01110         }
01111     if( rect.right() < area.left() + 5 || rect.left() > area.right() - 5 )
01112         { // not visible (almost) at all - try to make it at least partially visible
01113         if( isMovable())
01114             {
01115             if( rect.left() < area.left() + 5 )
01116                 rect.moveRight( area.left() + 5 );
01117             if( rect.right() > area.right() - 5 )
01118                 rect.moveLeft( area.right() - 5 );
01119             }
01120         }
01121     }
01122 
01126 QSize Client::adjustedSize( const QSize& frame, Sizemode mode ) const
01127     {
01128     // first, get the window size for the given frame size s
01129 
01130     QSize wsize( frame.width() - ( border_left + border_right ),
01131              frame.height() - ( border_top + border_bottom ));
01132     if( wsize.isEmpty())
01133         wsize = QSize( 1, 1 );
01134 
01135     return sizeForClientSize( wsize, mode, false );
01136     }
01137 
01138 // this helper returns proper size even if the window is shaded
01139 // see also the comment in Client::setGeometry()
01140 QSize Client::adjustedSize() const
01141     {
01142     return sizeForClientSize( clientSize());
01143     }
01144 
01153 QSize Client::sizeForClientSize( const QSize& wsize, Sizemode mode, bool noframe ) const
01154     {
01155     int w = wsize.width();
01156     int h = wsize.height();
01157     if( w < 1 || h < 1 )
01158         {
01159         kWarning(1212) << "sizeForClientSize() with empty size!" ;
01160         kWarning(1212) << kBacktrace() ;
01161         }
01162     if (w<1) w = 1;
01163     if (h<1) h = 1;
01164 
01165     // basesize, minsize, maxsize, paspect and resizeinc have all values defined,
01166     // even if they're not set in flags - see getWmNormalHints()
01167     QSize min_size = minSize();
01168     QSize max_size = maxSize();
01169     if( decoration != NULL )
01170         {
01171         QSize decominsize = decoration->minimumSize();
01172         QSize border_size( border_left + border_right, border_top + border_bottom );
01173         if( border_size.width() > decominsize.width()) // just in case
01174             decominsize.setWidth( border_size.width());
01175         if( border_size.height() > decominsize.height())
01176             decominsize.setHeight( border_size.height());
01177         if( decominsize.width() > min_size.width())
01178                 min_size.setWidth( decominsize.width());
01179         if( decominsize.height() > min_size.height())
01180                 min_size.setHeight( decominsize.height());
01181         }
01182     w = qMin( max_size.width(), w );
01183     h = qMin( max_size.height(), h );
01184     w = qMax( min_size.width(), w );
01185     h = qMax( min_size.height(), h );
01186 
01187     int w1 = w;
01188     int h1 = h;
01189     int width_inc = xSizeHint.width_inc;
01190     int height_inc = xSizeHint.height_inc;
01191     int basew_inc = xSizeHint.min_width; // see getWmNormalHints()
01192     int baseh_inc = xSizeHint.min_height;
01193     w = int(( w - basew_inc ) / width_inc ) * width_inc + basew_inc;
01194     h = int(( h - baseh_inc ) / height_inc ) * height_inc + baseh_inc;
01195 // code for aspect ratios based on code from FVWM
01196     /*
01197      * The math looks like this:
01198      *
01199      * minAspectX    dwidth     maxAspectX
01200      * ---------- <= ------- <= ----------
01201      * minAspectY    dheight    maxAspectY
01202      *
01203      * If that is multiplied out, then the width and height are
01204      * invalid in the following situations:
01205      *
01206      * minAspectX * dheight > minAspectY * dwidth
01207      * maxAspectX * dheight < maxAspectY * dwidth
01208      *
01209      */
01210     if( xSizeHint.flags & PAspect )
01211         {
01212         double min_aspect_w = xSizeHint.min_aspect.x; // use doubles, because the values can be MAX_INT
01213         double min_aspect_h = xSizeHint.min_aspect.y; // and multiplying would go wrong otherwise
01214         double max_aspect_w = xSizeHint.max_aspect.x;
01215         double max_aspect_h = xSizeHint.max_aspect.y;
01216         // According to ICCCM 4.1.2.3 PMinSize should be a fallback for PBaseSize for size increments,
01217         // but not for aspect ratio. Since this code comes from FVWM, handles both at the same time,
01218         // and I have no idea how it works, let's hope nobody relies on that.
01219         w -= xSizeHint.base_width;
01220         h -= xSizeHint.base_height;
01221         int max_width = max_size.width() - xSizeHint.base_width;
01222         int min_width = min_size.width() - xSizeHint.base_width;
01223         int max_height = max_size.height() - xSizeHint.base_height;
01224         int min_height = min_size.height() - xSizeHint.base_height;
01225 #define ASPECT_CHECK_GROW_W \
01226         if( min_aspect_w * h > min_aspect_h * w ) \
01227             { \
01228             int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
01229             if( w + delta <= max_width ) \
01230                 w += delta; \
01231             }
01232 #define ASPECT_CHECK_SHRINK_H_GROW_W \
01233         if( min_aspect_w * h > min_aspect_h * w ) \
01234             { \
01235             int delta = int( h - w * min_aspect_h / min_aspect_w ) / height_inc * height_inc; \
01236             if( h - delta >= min_height ) \
01237                 h -= delta; \
01238             else \
01239                 { \
01240                 int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
01241                 if( w + delta <= max_width ) \
01242                     w += delta; \
01243                 } \
01244             }
01245 #define ASPECT_CHECK_GROW_H \
01246         if( max_aspect_w * h < max_aspect_h * w ) \
01247             { \
01248             int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
01249             if( h + delta <= max_height ) \
01250                 h += delta; \
01251             }
01252 #define ASPECT_CHECK_SHRINK_W_GROW_H \
01253         if( max_aspect_w * h < max_aspect_h * w ) \
01254             { \
01255             int delta = int( w - max_aspect_w * h / max_aspect_h ) / width_inc * width_inc; \
01256             if( w - delta >= min_width ) \
01257                 w -= delta; \
01258             else \
01259                 { \
01260                 int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
01261                 if( h + delta <= max_height ) \
01262                     h += delta; \
01263                 } \
01264             }
01265         switch( mode )
01266             {
01267             case SizemodeAny:
01268 #if 0 // make SizemodeAny equal to SizemodeFixedW - prefer keeping fixed width,
01269       // so that changing aspect ratio to a different value and back keeps the same size (#87298)
01270                 {
01271                 ASPECT_CHECK_SHRINK_H_GROW_W
01272                 ASPECT_CHECK_SHRINK_W_GROW_H
01273                 ASPECT_CHECK_GROW_H
01274                 ASPECT_CHECK_GROW_W
01275                 break;
01276                 }
01277 #endif
01278             case SizemodeFixedW:
01279                 {
01280                 // the checks are order so that attempts to modify height are first
01281                 ASPECT_CHECK_GROW_H
01282                 ASPECT_CHECK_SHRINK_H_GROW_W
01283                 ASPECT_CHECK_SHRINK_W_GROW_H
01284                 ASPECT_CHECK_GROW_W
01285                 break;
01286                 }
01287             case SizemodeFixedH:
01288                 {
01289                 ASPECT_CHECK_GROW_W
01290                 ASPECT_CHECK_SHRINK_W_GROW_H
01291                 ASPECT_CHECK_SHRINK_H_GROW_W
01292                 ASPECT_CHECK_GROW_H
01293                 break;
01294                 }
01295             case SizemodeMax:
01296                 {
01297                 // first checks that try to shrink
01298                 ASPECT_CHECK_SHRINK_H_GROW_W
01299                 ASPECT_CHECK_SHRINK_W_GROW_H
01300                 ASPECT_CHECK_GROW_W
01301                 ASPECT_CHECK_GROW_H
01302                 break;
01303                 }
01304             }
01305 #undef ASPECT_CHECK_SHRINK_H_GROW_W
01306 #undef ASPECT_CHECK_SHRINK_W_GROW_H
01307 #undef ASPECT_CHECK_GROW_W
01308 #undef ASPECT_CHECK_GROW_H
01309         w += xSizeHint.base_width;
01310         h += xSizeHint.base_height;
01311         }
01312     if( !rules()->checkStrictGeometry( false ))
01313         {
01314         // disobey increments and aspect when maximized
01315         if( maximizeMode() & MaximizeHorizontal )
01316             w = w1;
01317         if( maximizeMode() & MaximizeVertical )
01318             h = h1;
01319         }
01320 
01321     if( !noframe )
01322         {
01323         w += border_left + border_right;
01324         h += border_top + border_bottom;
01325         }
01326     return rules()->checkSize( QSize( w, h ));
01327     }
01328 
01332 void Client::getWmNormalHints()
01333     {
01334     long msize;
01335     if (XGetWMNormalHints(display(), window(), &xSizeHint, &msize) == 0 )
01336         xSizeHint.flags = 0;
01337     // set defined values for the fields, even if they're not in flags
01338 
01339     if( ! ( xSizeHint.flags & PMinSize ))
01340         xSizeHint.min_width = xSizeHint.min_height = 0;
01341     if( xSizeHint.flags & PBaseSize )
01342         {
01343         // PBaseSize is a fallback for PMinSize according to ICCCM 4.1.2.3
01344         // The other way around PMinSize is not a complete fallback for PBaseSize,
01345         // so that's not handled here.
01346         if( ! ( xSizeHint.flags & PMinSize ))
01347             {
01348             xSizeHint.min_width = xSizeHint.base_width;
01349             xSizeHint.min_height = xSizeHint.base_height;
01350             }
01351         }
01352     else
01353         xSizeHint.base_width = xSizeHint.base_height = 0;
01354     if( ! ( xSizeHint.flags & PMaxSize ))
01355         xSizeHint.max_width = xSizeHint.max_height = INT_MAX;
01356     else
01357         {
01358         xSizeHint.max_width = qMax( xSizeHint.max_width, 1 );
01359         xSizeHint.max_height = qMax( xSizeHint.max_height, 1 );
01360         }
01361     if( xSizeHint.flags & PResizeInc )
01362         {
01363         xSizeHint.width_inc = qMax( xSizeHint.width_inc, 1 );
01364         xSizeHint.height_inc = qMax( xSizeHint.height_inc, 1 );
01365         }
01366     else
01367         {
01368         xSizeHint.width_inc = 1;
01369         xSizeHint.height_inc = 1;
01370         }
01371     if( xSizeHint.flags & PAspect )
01372         { // no dividing by zero
01373         xSizeHint.min_aspect.y = qMax( xSizeHint.min_aspect.y, 1 );
01374         xSizeHint.max_aspect.y = qMax( xSizeHint.max_aspect.y, 1 );
01375         }
01376     else
01377         {
01378         xSizeHint.min_aspect.x = 1;
01379         xSizeHint.min_aspect.y = INT_MAX;
01380         xSizeHint.max_aspect.x = INT_MAX;
01381         xSizeHint.max_aspect.y = 1;
01382         }
01383     if( ! ( xSizeHint.flags & PWinGravity ))
01384         xSizeHint.win_gravity = NorthWestGravity;
01385     if( isManaged())
01386         { // update to match restrictions
01387         QSize new_size = adjustedSize();
01388         if( new_size != size() && !isFullScreen())
01389             {
01390             QRect orig_geometry = geometry();
01391             resizeWithChecks( new_size );
01392             if( ( !isSpecialWindow() || isToolbar()) && !isFullScreen())
01393                 {
01394                 // try to keep the window in its xinerama screen if possible,
01395                 // if that fails at least keep it visible somewhere
01396                 QRect area = workspace()->clientArea( MovementArea, this );
01397                 if( area.contains( orig_geometry ))
01398                     keepInArea( area );
01399                 area = workspace()->clientArea( WorkArea, this );
01400                 if( area.contains( orig_geometry ))
01401                     keepInArea( area );
01402                 }
01403             }
01404         }
01405     updateAllowedActions(); // affects isResizeable()
01406     }
01407 
01408 QSize Client::minSize() const
01409     {
01410     return rules()->checkMinSize( QSize( xSizeHint.min_width, xSizeHint.min_height ));
01411     }
01412 
01413 QSize Client::maxSize() const
01414     {
01415     return rules()->checkMaxSize( QSize( xSizeHint.max_width, xSizeHint.max_height ));
01416     }
01417 
01423 void Client::sendSyntheticConfigureNotify()
01424     {
01425     XConfigureEvent c;
01426     c.type = ConfigureNotify;
01427     c.send_event = True;
01428     c.event = window();
01429     c.window = window();
01430     c.x = x() + clientPos().x();
01431     c.y = y() + clientPos().y();
01432     c.width = clientSize().width();
01433     c.height = clientSize().height();
01434     c.border_width = 0;
01435     c.above = None;
01436     c.override_redirect = 0;
01437     XSendEvent( display(), c.event, true, StructureNotifyMask, (XEvent*)&c );
01438     }
01439 
01440 const QPoint Client::calculateGravitation( bool invert, int gravity ) const
01441     {
01442     int dx, dy;
01443     dx = dy = 0;
01444 
01445     if( gravity == 0 ) // default (nonsense) value for the argument
01446         gravity = xSizeHint.win_gravity;
01447 
01448 // dx, dy specify how the client window moves to make space for the frame
01449     switch (gravity)
01450         {
01451         case NorthWestGravity: // move down right
01452         default:
01453             dx = border_left;
01454             dy = border_top;
01455             break;
01456         case NorthGravity: // move right
01457             dx = 0;
01458             dy = border_top;
01459             break;
01460         case NorthEastGravity: // move down left
01461             dx = -border_right;
01462             dy = border_top;
01463             break;
01464         case WestGravity: // move right
01465             dx = border_left;
01466             dy = 0;
01467             break;
01468         case CenterGravity:
01469             break; // will be handled specially
01470         case StaticGravity: // don't move
01471             dx = 0;
01472             dy = 0;
01473             break;
01474         case EastGravity: // move left
01475             dx = -border_right;
01476             dy = 0;
01477             break;
01478         case SouthWestGravity: // move up right
01479             dx = border_left ;
01480             dy = -border_bottom;
01481             break;
01482         case SouthGravity: // move up
01483             dx = 0;
01484             dy = -border_bottom;
01485             break;
01486         case SouthEastGravity: // move up left
01487             dx = -border_right;
01488             dy = -border_bottom;
01489             break;
01490         }
01491     if( gravity != CenterGravity )
01492         { // translate from client movement to frame movement
01493         dx -= border_left;
01494         dy -= border_top;
01495         }
01496     else
01497         { // center of the frame will be at the same position client center without frame would be
01498         dx = - ( border_left + border_right ) / 2;
01499         dy = - ( border_top + border_bottom ) / 2;
01500         }
01501     if( !invert )
01502         return QPoint( x() + dx, y() + dy );
01503     else
01504         return QPoint( x() - dx, y() - dy );
01505     }
01506 
01507 void Client::configureRequest( int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool )
01508     {
01509     if( gravity == 0 ) // default (nonsense) value for the argument
01510         gravity = xSizeHint.win_gravity;
01511     if( value_mask & ( CWX | CWY ))
01512         {
01513         QPoint new_pos = calculateGravitation( true, gravity ); // undo gravitation
01514         if ( value_mask & CWX )
01515             new_pos.setX( rx );
01516         if ( value_mask & CWY )
01517             new_pos.setY( ry );
01518 
01519         // clever(?) workaround for applications like xv that want to set
01520         // the location to the current location but miscalculate the
01521         // frame size due to kwin being a double-reparenting window
01522         // manager
01523         if ( new_pos.x() == x() + clientPos().x() && new_pos.y() == y() + clientPos().y()
01524             && gravity == NorthWestGravity && !from_tool )
01525             {
01526             new_pos.setX( x());
01527             new_pos.setY( y());
01528             }
01529 
01530         int nw = clientSize().width();
01531         int nh = clientSize().height();
01532         if ( value_mask & CWWidth )
01533             nw = rw;
01534         if ( value_mask & CWHeight )
01535             nh = rh;
01536         QSize ns = sizeForClientSize( QSize( nw, nh ) ); // enforces size if needed
01537         new_pos = rules()->checkPosition( new_pos );
01538 
01539         // TODO what to do with maximized windows?
01540         if ( maximizeMode() != MaximizeFull
01541             || ns != size())
01542             {
01543             QRect orig_geometry = geometry();
01544             GeometryUpdatesBlocker blocker( this );
01545             move( new_pos );
01546             plainResize( ns );
01547             setGeometry( QRect( calculateGravitation( false, gravity ), size()));
01548             updateFullScreenHack( QRect( new_pos, QSize( nw, nh )));
01549             QRect area = workspace()->clientArea( WorkArea, this );
01550             if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen()
01551                 && area.contains( orig_geometry ))
01552                 keepInArea( area );
01553 
01554             // this is part of the kicker-xinerama-hack... it should be
01555             // safe to remove when kicker gets proper ExtendedStrut support;
01556             // see Workspace::updateClientArea() and
01557             // Client::adjustedClientArea()
01558             if (hasStrut ())
01559                 workspace() -> updateClientArea ();
01560             }
01561         }
01562 
01563     if ( value_mask & (CWWidth | CWHeight )
01564         && ! ( value_mask & ( CWX | CWY )) )  // pure resize
01565         {
01566         int nw = clientSize().width();
01567         int nh = clientSize().height();
01568         if ( value_mask & CWWidth )
01569             nw = rw;
01570         if ( value_mask & CWHeight )
01571             nh = rh;
01572         QSize ns = sizeForClientSize( QSize( nw, nh ) );
01573 
01574         if( ns != size())  // don't restore if some app sets its own size again
01575             {
01576             QRect orig_geometry = geometry();
01577             GeometryUpdatesBlocker blocker( this );
01578             int save_gravity = xSizeHint.win_gravity;
01579             xSizeHint.win_gravity = gravity;
01580             resizeWithChecks( ns );
01581             xSizeHint.win_gravity = save_gravity;
01582             updateFullScreenHack( QRect( calculateGravitation( true, xSizeHint.win_gravity ), QSize( nw, nh )));
01583             if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen())
01584                 {
01585                 // try to keep the window in its xinerama screen if possible,
01586                 // if that fails at least keep it visible somewhere
01587                 QRect area = workspace()->clientArea( MovementArea, this );
01588                 if( area.contains( orig_geometry ))
01589                     keepInArea( area );
01590                 area = workspace()->clientArea( WorkArea, this );
01591                 if( area.contains( orig_geometry ))
01592                     keepInArea( area );
01593                 }
01594             }
01595         }
01596     // No need to send synthetic configure notify event here, either it's sent together
01597     // with geometry change, or there's no need to send it.
01598     // Handling of the real ConfigureRequest event forces sending it, as there it's necessary.
01599     }
01600 
01601 void Client::resizeWithChecks( int w, int h, ForceGeometry_t force )
01602     {
01603     assert( !shade_geometry_change );
01604     if( isShade())
01605         {
01606         if( h == border_top + border_bottom )
01607             {
01608             kWarning(1212) << "Shaded geometry passed for size:" ;
01609             kWarning(1212) << kBacktrace() ;
01610             }
01611         }
01612     int newx = x();
01613     int newy = y();
01614     QRect area = workspace()->clientArea( WorkArea, this );
01615     // don't allow growing larger than workarea
01616     if( w > area.width())
01617         w = area.width();
01618     if( h > area.height())
01619         h = area.height();
01620     QSize tmp = adjustedSize( QSize( w, h )); // checks size constraints, including min/max size
01621     w = tmp.width();
01622     h = tmp.height();
01623     switch( xSizeHint.win_gravity )
01624         {
01625         case NorthWestGravity: // top left corner doesn't move
01626         default:
01627             break;
01628         case NorthGravity: // middle of top border doesn't move
01629             newx = ( newx + width() / 2 ) - ( w / 2 );
01630             break;
01631         case NorthEastGravity: // top right corner doesn't move
01632             newx = newx + width() - w;
01633             break;
01634         case WestGravity: // middle of left border doesn't move
01635             newy = ( newy + height() / 2 ) - ( h / 2 );
01636             break;
01637         case CenterGravity: // middle point doesn't move
01638             newx = ( newx + width() / 2 ) - ( w / 2 );
01639             newy = ( newy + height() / 2 ) - ( h / 2 );
01640             break;
01641         case StaticGravity: // top left corner of _client_ window doesn't move
01642             // since decoration doesn't change, equal to NorthWestGravity
01643             break;
01644         case EastGravity: // // middle of right border doesn't move
01645             newx = newx + width() - w;
01646             newy = ( newy + height() / 2 ) - ( h / 2 );
01647             break;
01648         case SouthWestGravity: // bottom left corner doesn't move
01649             newy = newy + height() - h;
01650             break;
01651         case SouthGravity: // middle of bottom border doesn't move
01652             newx = ( newx + width() / 2 ) - ( w / 2 );
01653             newy = newy + height() - h;
01654             break;
01655         case SouthEastGravity: // bottom right corner doesn't move
01656             newx = newx + width() - w;
01657             newy = newy + height() - h;
01658             break;
01659         }
01660     // if it would be moved outside of workarea, keep it inside,
01661     // see also Client::computeWorkareaDiff()
01662     if( workarea_diff_x != INT_MIN && w <= area.width()) // was inside and can still fit
01663         {
01664         if( newx < area.left())
01665             newx = area.left();
01666         if( newx + w > area.right() + 1 )
01667             newx = area.right() + 1 - w;
01668         assert( newx >= area.left() && newx + w <= area.right() + 1 ); // width was checked above
01669         }
01670     if( workarea_diff_y != INT_MIN && h <= area.height()) // was inside and can still fit
01671         {
01672         if( newy < area.top())
01673             newy = area.top();
01674         if( newy + h > area.bottom() + 1 )
01675             newy = area.bottom() + 1 - h;
01676         assert( newy >= area.top() && newy + h <= area.bottom() + 1 ); // height was checked above
01677         }
01678     setGeometry( newx, newy, w, h, force );
01679     }
01680 
01681 // _NET_MOVERESIZE_WINDOW
01682 void Client::NETMoveResizeWindow( int flags, int x, int y, int width, int height )
01683     {
01684     int gravity = flags & 0xff;
01685     int value_mask = 0;
01686     if( flags & ( 1 << 8 ))
01687         value_mask |= CWX;
01688     if( flags & ( 1 << 9 ))
01689         value_mask |= CWY;
01690     if( flags & ( 1 << 10 ))
01691         value_mask |= CWWidth;
01692     if( flags & ( 1 << 11 ))
01693         value_mask |= CWHeight;
01694     configureRequest( value_mask, x, y, width, height, gravity, true );
01695     }
01696 
01701 bool Client::isMovable() const
01702     {
01703     if( !motif_may_move || isFullScreen())
01704         return false;
01705     if( isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :)
01706         return false;
01707     if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
01708         return false;
01709     if( rules()->checkPosition( invalidPoint ) != invalidPoint ) // forced position
01710         return false;
01711     return true;
01712     }
01713 
01717 bool Client::isMovableAcrossScreens() const
01718     {
01719     if( !motif_may_move )
01720         return false;
01721     if( isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :)
01722         return false;
01723     if( rules()->checkPosition( invalidPoint ) != invalidPoint ) // forced position
01724         return false;
01725     return true;
01726     }
01727 
01731 bool Client::isResizable() const
01732     {
01733     if( !motif_may_resize || isFullScreen())
01734         return false;
01735     if( isSpecialWindow() || isSplash() || isToolbar())
01736         return false;
01737     if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
01738         return false;
01739     if( rules()->checkSize( QSize()).isValid()) // forced size
01740         return false;
01741 
01742     QSize min = minSize();
01743     QSize max = maxSize();
01744     return min.width() < max.width() || min.height() < max.height();
01745     }
01746 
01747 /*
01748   Returns whether the window is maximizable or not
01749  */
01750 bool Client::isMaximizable() const
01751     {
01752         { // isMovable() and isResizable() may be false for maximized windows
01753           // with moving/resizing maximized windows disabled
01754         TemporaryAssign< MaximizeMode > tmp( max_mode, MaximizeRestore );
01755         if( !isMovable() || !isResizable() || isToolbar()) // SELI isToolbar() ?
01756             return false;
01757         }
01758     if ( maximizeMode() != MaximizeRestore )
01759         return true;
01760     QSize max = maxSize();
01761 #if 0
01762     if( max.width() < 32767 || max.height() < 32767 ) // sizes are 16bit with X
01763         return false;
01764 #else
01765     // apparently there are enough apps which specify some arbitrary value
01766     // for their maximum size just for the fun of it
01767     QSize areasize = workspace()->clientArea( MaximizeArea, this ).size();
01768     if( max.width() < areasize.width() || max.height() < areasize.height())
01769         return false;
01770 #endif
01771     return true;
01772     }
01773 
01774 
01778 void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force )
01779     {
01780     // this code is also duplicated in Client::plainResize()
01781     // Ok, the shading geometry stuff. Generally, code doesn't care about shaded geometry,
01782     // simply because there are too many places dealing with geometry. Those places
01783     // ignore shaded state and use normal geometry, which they usually should get
01784     // from adjustedSize(). Such geometry comes here, and if the window is shaded,
01785     // the geometry is used only for client_size, since that one is not used when
01786     // shading. Then the frame geometry is adjusted for the shaded geometry.
01787     // This gets more complicated in the case the code does only something like
01788     // setGeometry( geometry()) - geometry() will return the shaded frame geometry.
01789     // Such code is wrong and should be changed to handle the case when the window is shaded,
01790     // for example using Client::clientSize().
01791     if( shade_geometry_change )
01792         ; // nothing
01793     else if( isShade())
01794         {
01795         if( h == border_top + border_bottom )
01796             {
01797             kDebug(1212) << "Shaded geometry passed for size:";
01798             kDebug(1212) << kBacktrace();
01799             }
01800         else
01801             {
01802             client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
01803             h = border_top + border_bottom;
01804             }
01805         }
01806     else
01807         {
01808         client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
01809         }
01810     QRect g( x, y, w, h );
01811     if( block_geometry_updates == 0 && g != rules()->checkGeometry( g ))
01812         {
01813         kDebug(1212) << "forced geometry fail:" << g << ":" << rules()->checkGeometry( g );
01814         kDebug(1212) << kBacktrace();
01815         }
01816     if( force == NormalGeometrySet && geom == g && pending_geometry_update == PendingGeometryNone )
01817         return;
01818     geom = g;
01819     updateWorkareaDiffs();
01820     if( block_geometry_updates != 0 )
01821         {
01822         if( pending_geometry_update == PendingGeometryForced )
01823             {} // maximum, nothing needed
01824         else if( force == ForceGeometrySet )
01825             pending_geometry_update = PendingGeometryForced;
01826         else
01827             pending_geometry_update = PendingGeometryNormal;
01828         return;
01829         }
01830     bool resized = ( geom_before_block.size() != geom.size() || pending_geometry_update == PendingGeometryForced );
01831     if( resized )
01832         {
01833         resizeDecoration( QSize( w, h ));
01834         XMoveResizeWindow( display(), frameId(), x, y, w, h );
01835         if( !isShade())
01836             {
01837             QSize cs = clientSize();
01838             XMoveResizeWindow( display(), wrapperId(), clientPos().x(), clientPos().y(),
01839                 cs.width(), cs.height());
01840             XMoveResizeWindow( display(), window(), 0, 0, cs.width(), cs.height());
01841             }
01842         updateShape();
01843         }
01844     else
01845         XMoveWindow( display(), frameId(), x, y );
01846     // SELI TODO won't this be too expensive?
01847     sendSyntheticConfigureNotify();
01848     updateWindowRules();
01849     checkMaximizeGeometry();
01850     workspace()->checkActiveScreen( this );
01851     workspace()->updateStackingOrder();
01852     workspace()->checkUnredirect();
01853     if( resized )
01854         {
01855         discardWindowPixmap();
01856         if( scene != NULL )
01857             scene->windowGeometryShapeChanged( this );
01858         if( effects != NULL )
01859             static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), geom_before_block );
01860         }
01861     addWorkspaceRepaint( geom_before_block );
01862     addWorkspaceRepaint( geom );
01863     geom_before_block = geom;
01864     }
01865 
01866 void Client::plainResize( int w, int h, ForceGeometry_t force )
01867     {
01868     // this code is also duplicated in Client::setGeometry(), and it's also commented there
01869     if( shade_geometry_change )
01870         ; // nothing
01871     else if( isShade())
01872         {
01873         if( h == border_top + border_bottom )
01874             {
01875             kDebug(1212) << "Shaded geometry passed for size:";
01876             kDebug(1212) << kBacktrace();
01877             }
01878         else
01879             {
01880             client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
01881             h = border_top + border_bottom;
01882             }
01883         }
01884     else
01885         {
01886         client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
01887         }
01888     QSize s( w, h );
01889     if( block_geometry_updates == 0 && s != rules()->checkSize( s ))
01890         {
01891         kDebug(1212) << "forced size fail:" << s << ":" << rules()->checkSize( s );
01892         kDebug(1212) << kBacktrace();
01893         }
01894     // resuming geometry updates is handled only in setGeometry()
01895     assert( pending_geometry_update == PendingGeometryNone || block_geometry_updates > 0 );
01896     if( force == NormalGeometrySet && geom.size() == s )
01897         return;
01898     geom.setSize( s );
01899     updateWorkareaDiffs();
01900     if( block_geometry_updates != 0 )
01901         {
01902         if( pending_geometry_update == PendingGeometryForced )
01903             {} // maximum, nothing needed
01904         else if( force == ForceGeometrySet )
01905             pending_geometry_update = PendingGeometryForced;
01906         else
01907             pending_geometry_update = PendingGeometryNormal;
01908         return;
01909         }
01910     resizeDecoration( s );
01911     XResizeWindow( display(), frameId(), w, h );
01912 //     resizeDecoration( s );
01913     if( !isShade())
01914         {
01915         QSize cs = clientSize();
01916         XMoveResizeWindow( display(), wrapperId(), clientPos().x(), clientPos().y(),
01917             cs.width(), cs.height());
01918         XMoveResizeWindow( display(), window(), 0, 0, cs.width(), cs.height());
01919         }
01920     updateShape();
01921     sendSyntheticConfigureNotify();
01922     updateWindowRules();
01923     checkMaximizeGeometry();
01924     workspace()->checkActiveScreen( this );
01925     workspace()->updateStackingOrder();
01926     workspace()->checkUnredirect();
01927     discardWindowPixmap();
01928     if( scene != NULL )
01929         scene->windowGeometryShapeChanged( this );
01930     if( effects != NULL )
01931         static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), geom_before_block );
01932     addWorkspaceRepaint( geom_before_block );
01933     addWorkspaceRepaint( geom );
01934     geom_before_block = geom;
01935     }
01936 
01940 void Client::move( int x, int y, ForceGeometry_t force )
01941     {
01942     // resuming geometry updates is handled only in setGeometry()
01943     assert( pending_geometry_update == PendingGeometryNone || block_geometry_updates > 0 );
01944     QPoint p( x, y );
01945     if( block_geometry_updates == 0 && p != rules()->checkPosition( p ))
01946         {
01947         kDebug(1212) << "forced position fail:" << p << ":" << rules()->checkPosition( p );
01948         kDebug(1212) << kBacktrace();
01949         }
01950     if( force == NormalGeometrySet && geom.topLeft() == p )
01951         return;
01952     geom.moveTopLeft( p );
01953     updateWorkareaDiffs();
01954     if( block_geometry_updates != 0 )
01955         {
01956         if( pending_geometry_update == PendingGeometryForced )
01957             {} // maximum, nothing needed
01958         else if( force == ForceGeometrySet )
01959             pending_geometry_update = PendingGeometryForced;
01960         else
01961             pending_geometry_update = PendingGeometryNormal;
01962         return;
01963         }
01964     XMoveWindow( display(), frameId(), x, y );
01965     sendSyntheticConfigureNotify();
01966     updateWindowRules();
01967     checkMaximizeGeometry();
01968     workspace()->checkActiveScreen( this );
01969     workspace()->updateStackingOrder();
01970     workspace()->checkUnredirect();
01971     // client itself is not damaged
01972     addWorkspaceRepaint( geom_before_block );
01973     addWorkspaceRepaint( geom ); // trigger repaint of window's new location
01974     geom_before_block = geom;
01975     }
01976 
01977 void Client::blockGeometryUpdates( bool block )
01978     {
01979     if( block )
01980         {
01981         if( block_geometry_updates == 0 )
01982             pending_geometry_update = PendingGeometryNone;
01983         ++block_geometry_updates;
01984         }
01985     else
01986         {
01987         if( --block_geometry_updates == 0 )
01988             {
01989             if( pending_geometry_update != PendingGeometryNone )
01990                 {
01991                 if( isShade())
01992                     setGeometry( QRect( pos(), adjustedSize()), NormalGeometrySet );
01993                 else
01994                     setGeometry( geometry(), NormalGeometrySet );
01995                 pending_geometry_update = PendingGeometryNone;
01996                 }
01997             }
01998         }
01999     }
02000 
02001 void Client::maximize( MaximizeMode m )
02002     {
02003     setMaximize( m & MaximizeVertical, m & MaximizeHorizontal );
02004     }
02005 
02009 void Client::setMaximize( bool vertically, bool horizontally )
02010     {   // changeMaximize() flips the state, so change from set->flip
02011     changeMaximize(
02012         max_mode & MaximizeVertical ? !vertically : vertically,
02013         max_mode & MaximizeHorizontal ? !horizontally : horizontally,
02014         false );
02015     }
02016 
02017 void Client::changeMaximize( bool vertical, bool horizontal, bool adjust )
02018     {
02019     if( !isMaximizable())
02020         return;
02021 
02022     MaximizeMode old_mode = max_mode;
02023     // 'adjust == true' means to update the size only, e.g. after changing workspace size
02024     if( !adjust )
02025         {
02026         if( vertical )
02027             max_mode = MaximizeMode( max_mode ^ MaximizeVertical );
02028         if( horizontal )
02029             max_mode = MaximizeMode( max_mode ^ MaximizeHorizontal );
02030         }
02031 
02032     max_mode = rules()->checkMaximize( max_mode );
02033     if( !adjust && max_mode == old_mode )
02034         return;
02035 
02036     GeometryUpdatesBlocker blocker( this );
02037 
02038     // maximing one way and unmaximizing the other way shouldn't happen,
02039     // so restore first and then maximize the other way
02040     if( ( old_mode == MaximizeVertical && max_mode == MaximizeHorizontal )
02041         || ( old_mode == MaximizeHorizontal && max_mode == MaximizeVertical ))
02042         {
02043         changeMaximize( false, false, false ); // restore
02044         }
02045 
02046 
02047     QRect clientArea = workspace()->clientArea( MaximizeArea, this );
02048 
02049     // save sizes for restoring, if maximalizing
02050     if( !adjust && !( y() == clientArea.top() && height() == clientArea.height()))
02051         {
02052         geom_restore.setTop( y());
02053         geom_restore.setHeight( height());
02054         }
02055     if( !adjust && !( x() == clientArea.left() && width() == clientArea.width()))
02056         {
02057         geom_restore.setLeft( x());
02058         geom_restore.setWidth( width());
02059         }
02060 
02061     if( !adjust )
02062         {
02063         if(( vertical && !(old_mode & MaximizeVertical ))
02064             || ( horizontal && !( old_mode & MaximizeHorizontal )))
02065             Notify::raise( Notify::Maximize );
02066         else
02067             Notify::raise( Notify::UnMaximize );
02068         }
02069 
02070     ForceGeometry_t geom_mode = NormalGeometrySet;
02071     if( decoration != NULL ) // decorations may turn off some borders when maximized
02072         {
02073         if( checkBorderSizes( false )) // only query, don't resize
02074             geom_mode = ForceGeometrySet;
02075         }
02076 
02077     // restore partial maximizations
02078     if ( old_mode==MaximizeFull && max_mode==MaximizeRestore )
02079         {
02080         if ( maximizeModeRestore()==MaximizeVertical )
02081         {
02082         max_mode = MaximizeVertical;
02083         maxmode_restore = MaximizeRestore;
02084         }
02085     if ( maximizeModeRestore()==MaximizeHorizontal )
02086         {
02087         max_mode = MaximizeHorizontal;
02088         maxmode_restore = MaximizeRestore;
02089         }
02090     }
02091 
02092     switch (max_mode)
02093         {
02094 
02095         case MaximizeVertical:
02096             {
02097             if( old_mode & MaximizeHorizontal ) // actually restoring from MaximizeFull
02098                 {
02099                 if( geom_restore.width() == 0 || !clientArea.contains( geom_restore.center() ))
02100                     { // needs placement
02101                     plainResize( adjustedSize( QSize( width() * 2 / 3, clientArea.height()), SizemodeFixedH ), geom_mode );
02102                     workspace()->placeSmart( this, clientArea );
02103                     }
02104                 else
02105                     {
02106                     setGeometry( QRect(QPoint( geom_restore.x(), clientArea.top()),
02107                         adjustedSize(QSize( geom_restore.width(), clientArea.height()), SizemodeFixedH )), geom_mode );
02108                     }
02109                 }
02110             else
02111                 {
02112                 setGeometry( QRect(QPoint(x(), clientArea.top()),
02113                     adjustedSize(QSize(width(), clientArea.height()), SizemodeFixedH )), geom_mode );
02114                 }
02115             info->setState( NET::MaxVert, NET::Max );
02116             break;
02117             }
02118 
02119         case MaximizeHorizontal:
02120             {
02121             if( old_mode & MaximizeVertical ) // actually restoring from MaximizeFull
02122                 {
02123                 if( geom_restore.height() == 0 || !clientArea.contains( geom_restore.center() ) )
02124                     { // needs placement
02125                     plainResize( adjustedSize( QSize( clientArea.width(), height() * 2 / 3 ), SizemodeFixedW ), geom_mode );
02126                     workspace()->placeSmart( this, clientArea );
02127                     }
02128                 else
02129                     {
02130                     setGeometry( QRect( QPoint(clientArea.left(), geom_restore.y()),
02131                         adjustedSize(QSize(clientArea.width(), geom_restore.height()), SizemodeFixedW )), geom_mode );
02132                     }
02133                 }
02134             else
02135                 {
02136                 setGeometry( QRect( QPoint(clientArea.left(), y()),
02137                     adjustedSize(QSize(clientArea.width(), height()), SizemodeFixedW )), geom_mode );
02138                 }
02139             info->setState( NET::MaxHoriz, NET::Max );
02140             break;
02141             }
02142 
02143         case MaximizeRestore:
02144             {
02145             QRect restore = geometry();
02146     // when only partially maximized, geom_restore may not have the other dimension remembered
02147             if( old_mode & MaximizeVertical )
02148                 {
02149                 restore.setTop( geom_restore.top());
02150                 restore.setBottom( geom_restore.bottom());
02151                 }
02152             if( old_mode & MaximizeHorizontal )
02153                 {
02154                 restore.setLeft( geom_restore.left());
02155                 restore.setRight( geom_restore.right());
02156                 }
02157             if( !restore.isValid())
02158                 {
02159                 QSize s = QSize( clientArea.width()*2/3, clientArea.height()*2/3 );
02160                 if( geom_restore.width() > 0 )
02161                     s.setWidth( geom_restore.width());
02162                 if( geom_restore.height() > 0 )
02163                     s.setHeight( geom_restore.height());
02164                 plainResize( adjustedSize( s ));
02165                 workspace()->placeSmart( this, clientArea );
02166                 restore = geometry();
02167                 if( geom_restore.width() > 0 )
02168                     restore.moveLeft( geom_restore.x());
02169                 if( geom_restore.height() > 0 )
02170                     restore.moveTop( geom_restore.y());
02171                 }
02172             setGeometry( restore, geom_mode );
02173             if( !clientArea.contains( geom_restore.center() )) // Not restoring to the same screen
02174                 workspace()->place( this, clientArea );
02175             info->setState( 0, NET::Max );
02176             break;
02177             }
02178 
02179         case MaximizeFull:
02180             {
02181             if( !adjust )
02182                 {
02183                 if( old_mode & MaximizeVertical )
02184                     maxmode_restore = MaximizeVertical;
02185                 if( old_mode & MaximizeHorizontal )
02186                     maxmode_restore = MaximizeHorizontal;
02187                 }
02188             QSize adjSize = adjustedSize(clientArea.size(), SizemodeMax );
02189             QRect r = QRect(clientArea.topLeft(), adjSize);
02190             setGeometry( r, geom_mode );
02191             info->setState( NET::Max, NET::Max );
02192             break;
02193             }
02194         default:
02195             break;
02196         }
02197 
02198     updateAllowedActions();
02199     if( decoration != NULL )
02200         decoration->maximizeChange();
02201     updateWindowRules();
02202     }
02203 
02204 void Client::resetMaximize()
02205     {
02206     if( max_mode == MaximizeRestore )
02207         return;
02208     max_mode = MaximizeRestore;
02209     Notify::raise( Notify::UnMaximize );
02210     info->setState( 0, NET::Max );
02211     updateAllowedActions();
02212     if( decoration != NULL )
02213         decoration->borders( border_left, border_right, border_top, border_bottom );
02214     if( isShade())
02215         setGeometry( QRect( pos(), sizeForClientSize( clientSize())), ForceGeometrySet );
02216     else
02217         setGeometry( geometry(), ForceGeometrySet );
02218     if( decoration != NULL )
02219         decoration->maximizeChange();
02220     }
02221 
02222 void Client::checkMaximizeGeometry()
02223     {
02224     // when adding new bail-out conditions here, checkMaximizeGeometry() needs to be called
02225     // when after the condition is no longer true
02226     if( isShade())
02227         return;
02228     if( isMove() || isResize()) // this is because of the option to disallow moving/resizing of max-ed windows
02229         return;
02230     // Just in case.
02231     static int recursion_protection = 0;
02232     if( recursion_protection > 3 )
02233         {
02234         kWarning( 1212 ) << "Check maximize overflow - you loose!" ;
02235         kWarning( 1212 ) << kBacktrace() ;
02236         return;
02237         }
02238     ++recursion_protection;
02239     QRect max_area = workspace()->clientArea( MaximizeArea, this );
02240     if( geometry() == max_area )
02241         {
02242         if( max_mode != MaximizeFull )
02243             maximize( MaximizeFull );
02244         }
02245     else if( x() == max_area.left() && width() == max_area.width())
02246         {
02247         if( max_mode != MaximizeHorizontal )
02248             maximize( MaximizeHorizontal );
02249         }
02250     else if( y() == max_area.top() && height() == max_area.height())
02251         {
02252         if( max_mode != MaximizeVertical )
02253             maximize( MaximizeVertical );
02254         }
02255     else if( max_mode != MaximizeRestore )
02256         {
02257         resetMaximize(); // not maximize( MaximizeRestore ), that'd change geometry - this is called from setGeometry()
02258         }
02259     --recursion_protection;
02260     }
02261 
02262 bool Client::isFullScreenable( bool fullscreen_hack ) const
02263     {
02264     if( !rules()->checkFullScreen( true ))
02265         return false;
02266     if( fullscreen_hack )
02267         return isNormalWindow();
02268     if( rules()->checkStrictGeometry( false ))
02269         {
02270         // the app wouldn't fit exactly fullscreen geometry due its strict geometry requirements
02271         QRect fsarea = workspace()->clientArea( FullScreenArea, this );
02272         if( sizeForClientSize( fsarea.size(), SizemodeAny, true ) != fsarea.size())
02273             return false;
02274         }
02275      // don't check size constrains - some apps request fullscreen despite requesting fixed size
02276     return !isSpecialWindow(); // also better disallow only weird types to go fullscreen
02277     }
02278 
02279 bool Client::userCanSetFullScreen() const
02280     {
02281     if( fullscreen_mode == FullScreenHack )
02282         return false;
02283     if( !isFullScreenable( false ))
02284         return false;
02285     // isMaximizable() returns false if fullscreen
02286     TemporaryAssign< FullScreenMode > tmp( fullscreen_mode, FullScreenNone );
02287     return isNormalWindow() && isMaximizable();
02288     }
02289 
02290 void Client::setFullScreen( bool set, bool user )
02291     {
02292     if( !isFullScreen() && !set )
02293         return;
02294     if( fullscreen_mode == FullScreenHack )
02295         return;
02296     if( user && !userCanSetFullScreen())
02297         return;
02298     set = rules()->checkFullScreen( set );
02299     setShade( ShadeNone );
02300     bool was_fs = isFullScreen();
02301     if( !was_fs )
02302         geom_fs_restore = geometry();
02303     fullscreen_mode = set ? FullScreenNormal : FullScreenNone;
02304     if( was_fs == isFullScreen())
02305         return;
02306     StackingUpdatesBlocker blocker1( workspace());
02307     GeometryUpdatesBlocker blocker2( this );
02308     workspace()->updateClientLayer( this ); // active fullscreens get different layer
02309     info->setState( isFullScreen() ? NET::FullScreen : 0, NET::FullScreen );
02310     updateDecoration( false, false );
02311     if( isFullScreen())
02312         if( info->fullscreenMonitors().isSet())
02313             setGeometry( fullscreenMonitorsArea( info->fullscreenMonitors()));
02314         else
02315             setGeometry( workspace()->clientArea( FullScreenArea, this ));
02316     else
02317         {
02318         if( !geom_fs_restore.isNull())
02319             setGeometry( QRect( geom_fs_restore.topLeft(), adjustedSize( geom_fs_restore.size())));
02320         // TODO isShaded() ?
02321         else
02322             { // does this ever happen?
02323             setGeometry( workspace()->clientArea( MaximizeArea, this ));
02324             }
02325         }
02326     updateWindowRules();
02327     workspace()->checkUnredirect();
02328     }
02329 
02330 
02331 void Client::updateFullscreenMonitors( NETFullscreenMonitors topology )
02332     {
02333     int nscreens = Kephal::ScreenUtils::numScreens();
02334 
02335 //    kDebug( 1212 ) << "incoming request with top: " << topology.top << " bottom: " << topology.bottom
02336 //                   << " left: " << topology.left << " right: " << topology.right
02337 //                   << ", we have: " << nscreens << " screens.";
02338 
02339     if( topology.top >= nscreens ||
02340         topology.bottom >= nscreens ||
02341         topology.left >= nscreens ||
02342         topology.right >= nscreens )
02343         {
02344             kWarning( 1212 ) << "fullscreenMonitors update failed. request higher than number of screens.";
02345             return;
02346         }
02347 
02348     info->setFullscreenMonitors( topology );
02349     if( isFullScreen())
02350         setGeometry( fullscreenMonitorsArea( topology ));
02351     }
02352 
02353 
02358 QRect Client::fullscreenMonitorsArea(NETFullscreenMonitors requestedTopology) const
02359     {
02360     QRect top, bottom, left, right, total;
02361 
02362     top = Kephal::ScreenUtils::screenGeometry( requestedTopology.top );
02363     bottom = Kephal::ScreenUtils::screenGeometry(requestedTopology.bottom );
02364     left = Kephal::ScreenUtils::screenGeometry(requestedTopology.left );
02365     right = Kephal::ScreenUtils::screenGeometry(requestedTopology.right );
02366     total = top.united( bottom.united( left.united( right ) ) );
02367 
02368 //    kDebug( 1212 ) << "top: " << top << " bottom: " << bottom
02369 //                   << " left: " << left << " right: " << right;
02370 //    kDebug( 1212 ) << "returning rect: " << total;
02371     return total;
02372     }
02373 
02374 
02375 int Client::checkFullScreenHack( const QRect& geom ) const
02376     {
02377     // if it's noborder window, and has size of one screen or the whole desktop geometry, it's fullscreen hack
02378     if( noBorder() && app_noborder && isFullScreenable( true ))
02379         {
02380         if( geom.size() == workspace()->clientArea( FullArea, geom.center(), desktop()).size())
02381             return 2; // full area fullscreen hack
02382         if( geom.size() == workspace()->clientArea( ScreenArea, geom.center(), desktop()).size())
02383             return 1; // xinerama-aware fullscreen hack
02384         }
02385     return 0;
02386     }
02387 
02388 void Client::updateFullScreenHack( const QRect& geom )
02389     {
02390     int type = checkFullScreenHack( geom );
02391     if( fullscreen_mode == FullScreenNone && type != 0 )
02392         {
02393         fullscreen_mode = FullScreenHack;
02394         updateDecoration( false, false );
02395         QRect geom;
02396         if( rules()->checkStrictGeometry( false ))
02397             {
02398             geom = type == 2 // 1 - it's xinerama-aware fullscreen hack, 2 - it's full area
02399                 ? workspace()->clientArea( FullArea, geom.center(), desktop())
02400                 : workspace()->clientArea( ScreenArea, geom.center(), desktop());
02401             }
02402         else
02403             geom = workspace()->clientArea( FullScreenArea, geom.center(), desktop());
02404         setGeometry( geom );
02405         }
02406     else if( fullscreen_mode == FullScreenHack && type == 0 )
02407         {
02408         fullscreen_mode = FullScreenNone;
02409         updateDecoration( false, false );
02410         // whoever called this must setup correct geometry
02411         }
02412     StackingUpdatesBlocker blocker( workspace());
02413     workspace()->updateClientLayer( this ); // active fullscreens get different layer
02414     }
02415 
02416 static QRect*       visible_bound  = 0;
02417 static GeometryTip* geometryTip    = 0;
02418 
02419 void Client::drawbound( const QRect& geom )
02420     {
02421     assert( visible_bound == NULL );
02422     visible_bound = new QRect( geom );
02423     doDrawbound( *visible_bound, false );
02424     }
02425 
02426 void Client::clearbound()
02427     {
02428     if( visible_bound == NULL )
02429         return;
02430     doDrawbound( *visible_bound, true );
02431     delete visible_bound;
02432     visible_bound = 0;
02433     }
02434 
02435 void Client::doDrawbound( const QRect& geom, bool clear )
02436     {
02437     if( decoration != NULL && decoration->drawbound( geom, clear ))
02438         return; // done by decoration
02439     XGCValues xgc;
02440     xgc.function = GXxor;
02441     xgc.foreground = WhitePixel( display(), DefaultScreen( display()));
02442     xgc.line_width = 5;
02443     xgc.subwindow_mode = IncludeInferiors;
02444     GC gc = XCreateGC( display(), DefaultRootWindow( display()),
02445         GCFunction | GCForeground | GCLineWidth | GCSubwindowMode, &xgc );
02446     // the line is 5 pixel thick, so compensate for the extra two pixels
02447     // on outside (#88657)
02448     QRect g = geom;
02449     if( g.width() > 5 )
02450         {
02451         g.setLeft( g.left() + 2 );
02452         g.setRight( g.right() - 2 );
02453         }
02454     if( g.height() > 5 )
02455         {
02456         g.setTop( g.top() + 2 );
02457         g.setBottom( g.bottom() - 2 );
02458         }
02459     XDrawRectangle( display(), DefaultRootWindow( display()), gc, g.x(), g.y(), g.width(), g.height());
02460     XFreeGC( display(), gc );
02461     }
02462 
02463 void Client::positionGeometryTip()
02464     {
02465     assert( isMove() || isResize());
02466     // Position and Size display
02467     if (options->showGeometryTip())
02468         {
02469         if( !geometryTip )
02470             { // save under is not necessary with opaque, and seem to make things slower
02471             bool save_under = ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
02472                         || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque );
02473             geometryTip = new GeometryTip( &xSizeHint, save_under );
02474             }
02475         QRect wgeom( moveResizeGeom ); // position of the frame, size of the window itself
02476         wgeom.setWidth( wgeom.width() - ( width() - clientSize().width()));
02477         wgeom.setHeight( wgeom.height() - ( height() - clientSize().height()));
02478         if( isShade())
02479             wgeom.setHeight( 0 );
02480         geometryTip->setGeometry( wgeom );
02481         if( !geometryTip->isVisible())
02482             geometryTip->show();
02483         geometryTip->raise();
02484         }
02485     }
02486 
02487 class EatAllPaintEvents
02488     : public QObject
02489     {
02490     protected:
02491         virtual bool eventFilter( QObject* o, QEvent* e )
02492             { return e->type() == QEvent::Paint && o != geometryTip; }
02493     };
02494 
02495 static EatAllPaintEvents* eater = 0;
02496 
02497 bool Client::startMoveResize()
02498     {
02499     assert( !moveResizeMode );
02500     assert( QWidget::keyboardGrabber() == NULL );
02501     assert( QWidget::mouseGrabber() == NULL );
02502     stopDelayedMoveResize();
02503     if( QApplication::activePopupWidget() != NULL )
02504         return false; // popups have grab
02505     bool has_grab = false;
02506     // This reportedly improves smoothness of the moveresize operation,
02507     // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug*
02508     // (http://lists.kde.org/?t=107302193400001&r=1&w=2)
02509     XSetWindowAttributes attrs;
02510     QRect r = workspace()->clientArea( FullArea, this );
02511     move_resize_grab_window = XCreateWindow( display(), rootWindow(), r.x(), r.y(),
02512         r.width(), r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, 0, &attrs );
02513     XMapRaised( display(), move_resize_grab_window );
02514     if( XGrabPointer( display(), move_resize_grab_window, False,
02515         ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask,
02516         GrabModeAsync, GrabModeAsync, move_resize_grab_window, cursor.handle(), xTime() ) == Success )
02517         has_grab = true;
02518     if( grabXKeyboard( frameId()))
02519         has_grab = move_resize_has_keyboard_grab = true;
02520     if( !has_grab ) // at least one grab is necessary in order to be able to finish move/resize
02521         {
02522         XDestroyWindow( display(), move_resize_grab_window );
02523         move_resize_grab_window = None;
02524         return false;
02525         }
02526     if ( maximizeMode() != MaximizeRestore )
02527         resetMaximize();
02528     moveResizeMode = true;
02529     workspace()->setClientIsMoving(this);
02530     initialMoveResizeGeom = moveResizeGeom = geometry();
02531     checkUnrestrictedMoveResize();
02532     if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
02533       || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
02534         {
02535         grabXServer();
02536         kapp->sendPostedEvents();
02537         // we have server grab -> nothing should cause paint events
02538         // unfortunately, that's not completely true, Qt may generate
02539         // paint events on some widgets due to FocusIn(?)
02540         // eat them, otherwise XOR painting will be broken (#58054)
02541         // paint events for the geometrytip need to be allowed, though
02542         eater = new EatAllPaintEvents;
02543 // not needed anymore?        kapp->installEventFilter( eater );
02544         }
02545     Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart );
02546     if( effects )
02547         static_cast<EffectsHandlerImpl*>(effects)->windowUserMovedResized( effectWindow(), true, false );
02548     if( options->electricBorders() == Options::ElectricMoveOnly )
02549         workspace()->reserveElectricBorderSwitching( true );
02550     return true;
02551     }
02552 
02553 void Client::finishMoveResize( bool cancel )
02554     {
02555     leaveMoveResize();
02556     if( cancel )
02557         setGeometry( initialMoveResizeGeom );
02558     else
02559         setGeometry( moveResizeGeom );
02560     checkMaximizeGeometry();
02561 // FRAME    update();
02562     Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd );
02563     if( effects )
02564         static_cast<EffectsHandlerImpl*>(effects)->windowUserMovedResized( effectWindow(), false, true );
02565     }
02566 
02567 void Client::leaveMoveResize()
02568     {
02569     clearbound();
02570     if (geometryTip)
02571         {
02572         geometryTip->hide();
02573         delete geometryTip;
02574         geometryTip = NULL;
02575         }
02576     if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
02577       || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
02578         ungrabXServer();
02579     if( move_resize_has_keyboard_grab )
02580         ungrabXKeyboard();
02581     move_resize_has_keyboard_grab = false;
02582     XUngrabPointer( display(), xTime() );
02583     XDestroyWindow( display(), move_resize_grab_window );
02584     move_resize_grab_window = None;
02585     workspace()->setClientIsMoving(0);
02586     if( move_faked_activity )
02587         workspace()->unfakeActivity( this );
02588     move_faked_activity = false;
02589     moveResizeMode = false;
02590     delete eater;
02591     eater = 0;
02592     delete sync_timeout;
02593     sync_timeout = NULL;
02594     if( options->electricBorders() == Options::ElectricMoveOnly )
02595         workspace()->reserveElectricBorderSwitching( false );
02596     }
02597 
02598 // This function checks if it actually makes sense to perform a restricted move/resize.
02599 // If e.g. the titlebar is already outside of the workarea, there's no point in performing
02600 // a restricted move resize, because then e.g. resize would also move the window (#74555).
02601 // NOTE: Most of it is duplicated from handleMoveResize().
02602 void Client::checkUnrestrictedMoveResize()
02603     {
02604     if( unrestrictedMoveResize )
02605         return;
02606     QRect desktopArea = workspace()->clientArea( WorkArea, moveResizeGeom.center(), desktop());
02607     int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
02608     // restricted move/resize - keep at least part of the titlebar always visible
02609     // how much must remain visible when moved away in that direction
02610     left_marge = qMin( 100 + border_right, moveResizeGeom.width());
02611     right_marge = qMin( 100 + border_left, moveResizeGeom.width());
02612     // width/height change with opaque resizing, use the initial ones
02613     titlebar_marge = initialMoveResizeGeom.height();
02614     top_marge = border_bottom;
02615     bottom_marge = border_top;
02616     if( isResize())
02617         {
02618         if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
02619             unrestrictedMoveResize = true;
02620         if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
02621             unrestrictedMoveResize = true;
02622         if( moveResizeGeom.right() < desktopArea.left() + left_marge )
02623             unrestrictedMoveResize = true;
02624         if( moveResizeGeom.left() > desktopArea.right() - right_marge )
02625             unrestrictedMoveResize = true;
02626         if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
02627             unrestrictedMoveResize = true;
02628         }
02629     if( isMove())
02630         {
02631         if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
02632             unrestrictedMoveResize = true;
02633         // no need to check top_marge, titlebar_marge already handles it
02634         if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
02635             unrestrictedMoveResize = true;
02636         if( moveResizeGeom.right() < desktopArea.left() + left_marge )
02637             unrestrictedMoveResize = true;
02638         if( moveResizeGeom.left() > desktopArea.right() - right_marge )
02639             unrestrictedMoveResize = true;
02640         }
02641     }
02642 
02643 // When the user pressed mouse on the titlebar, don't activate move immediatelly,
02644 // since it may be just a click. Activate instead after a delay. Move used to be
02645 // activated only after moving by several pixels, but that looks bad.
02646 void Client::startDelayedMoveResize()
02647     {
02648     delete delayedMoveResizeTimer;
02649     delayedMoveResizeTimer = new QTimer( this );
02650     connect( delayedMoveResizeTimer, SIGNAL( timeout()), this, SLOT( delayedMoveResize()));
02651     delayedMoveResizeTimer->setSingleShot( true );
02652     delayedMoveResizeTimer->start( QApplication::doubleClickInterval());
02653     }
02654 
02655 void Client::stopDelayedMoveResize()
02656     {
02657     delete delayedMoveResizeTimer;
02658     delayedMoveResizeTimer = NULL;
02659     }
02660 
02661 void Client::delayedMoveResize()
02662     {
02663     assert( buttonDown );
02664     if( !startMoveResize())
02665         buttonDown = false;
02666     updateCursor();
02667     stopDelayedMoveResize();
02668     }
02669 
02670 void Client::handleMoveResize( int x, int y, int x_root, int y_root )
02671     {
02672     if(( mode == PositionCenter && !isMovableAcrossScreens() )
02673         || ( mode != PositionCenter && ( isShade() || !isResizable())))
02674         return;
02675 
02676     if ( !moveResizeMode )
02677         {
02678         QPoint p( QPoint( x, y ) - moveOffset );
02679         if (p.manhattanLength() >= 6)
02680             {
02681             if( !startMoveResize())
02682                 {
02683                 buttonDown = false;
02684                 updateCursor();
02685                 return;
02686                 }
02687             updateCursor();
02688             }
02689         else
02690             return;
02691         }
02692 
02693     // ShadeHover or ShadeActive, ShadeNormal was already avoided above
02694     if ( mode != PositionCenter && shade_mode != ShadeNone )
02695         setShade( ShadeNone );
02696 
02697     QPoint globalPos( x_root, y_root );
02698     // these two points limit the geometry rectangle, i.e. if bottomleft resizing is done,
02699     // the bottomleft corner should be at is at (topleft.x(), bottomright().y())
02700     QPoint topleft = globalPos - moveOffset;
02701     QPoint bottomright = globalPos + invertedMoveOffset;
02702     QRect previousMoveResizeGeom = moveResizeGeom;
02703 
02704     // TODO move whole group when moving its leader or when the leader is not mapped?
02705 
02706     // compute bounds
02707     // NOTE: This is duped in checkUnrestrictedMoveResize().
02708     QRect desktopArea = workspace()->clientArea( WorkArea, globalPos, desktop());
02709     int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
02710     if( unrestrictedMoveResize ) // unrestricted, just don't let it go out completely
02711         left_marge = right_marge = top_marge = bottom_marge = titlebar_marge = 5;
02712     else // restricted move/resize - keep at least part of the titlebar always visible
02713         {
02714         // how much must remain visible when moved away in that direction
02715         left_marge = qMin( 100 + border_right, moveResizeGeom.width());
02716         right_marge = qMin( 100 + border_left, moveResizeGeom.width());
02717         // width/height change with opaque resizing, use the initial ones
02718         titlebar_marge = initialMoveResizeGeom.height();
02719         top_marge = border_bottom;
02720         bottom_marge = border_top;
02721         }
02722 
02723     bool update = false;
02724     if( isResize())
02725         {
02726         // first resize (without checking constrains), then snap, then check bounds, then check constrains
02727         QRect orig = initialMoveResizeGeom;
02728         Sizemode sizemode = SizemodeAny;
02729         switch ( mode )
02730             {
02731             case PositionTopLeft:
02732                 moveResizeGeom =  QRect( topleft, orig.bottomRight() ) ;
02733                 break;
02734             case PositionBottomRight:
02735                 moveResizeGeom =  QRect( orig.topLeft(), bottomright ) ;
02736                 break;
02737             case PositionBottomLeft:
02738                 moveResizeGeom =  QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ;
02739                 break;
02740             case PositionTopRight:
02741                 moveResizeGeom =  QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
02742                 break;
02743             case PositionTop:
02744                 moveResizeGeom =  QRect( QPoint( orig.left(), topleft.y() ), orig.bottomRight() ) ;
02745                 sizemode = SizemodeFixedH; // try not to affect height
02746                 break;
02747             case PositionBottom:
02748                 moveResizeGeom =  QRect( orig.topLeft(), QPoint( orig.right(), bottomright.y() ) ) ;
02749                 sizemode = SizemodeFixedH;
02750                 break;
02751             case PositionLeft:
02752                 moveResizeGeom =  QRect( QPoint( topleft.x(), orig.top() ), orig.bottomRight() ) ;
02753                 sizemode = SizemodeFixedW;
02754                 break;
02755             case PositionRight:
02756                 moveResizeGeom =  QRect( orig.topLeft(), QPoint( bottomright.x(), orig.bottom() ) ) ;
02757                 sizemode = SizemodeFixedW;
02758                 break;
02759             case PositionCenter:
02760             default:
02761                 abort();
02762                 break;
02763             }
02764 
02765         // adjust new size to snap to other windows/borders
02766         moveResizeGeom = workspace()->adjustClientSize( this, moveResizeGeom, mode );
02767 
02768         // NOTE: This is duped in checkUnrestrictedMoveResize().
02769         if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
02770             moveResizeGeom.setBottom( desktopArea.top() + top_marge );
02771         if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
02772             moveResizeGeom.setTop( desktopArea.bottom() - bottom_marge );
02773         if( moveResizeGeom.right() < desktopArea.left() + left_marge )
02774             moveResizeGeom.setRight( desktopArea.left() + left_marge );
02775         if( moveResizeGeom.left() > desktopArea.right() - right_marge )
02776             moveResizeGeom.setLeft(desktopArea.right() - right_marge );
02777         if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
02778             moveResizeGeom.setTop( desktopArea.top());
02779 
02780         QSize size = adjustedSize( moveResizeGeom.size(), sizemode );
02781         // the new topleft and bottomright corners (after checking size constrains), if they'll be needed
02782         topleft = QPoint( moveResizeGeom.right() - size.width() + 1, moveResizeGeom.bottom() - size.height() + 1 );
02783         bottomright = QPoint( moveResizeGeom.left() + size.width() - 1, moveResizeGeom.top() + size.height() - 1 );
02784         orig = moveResizeGeom;
02785         switch ( mode )
02786             { // these 4 corners ones are copied from above
02787             case PositionTopLeft:
02788                 moveResizeGeom =  QRect( topleft, orig.bottomRight() ) ;
02789                 break;
02790             case PositionBottomRight:
02791                 moveResizeGeom =  QRect( orig.topLeft(), bottomright ) ;
02792                 break;
02793             case PositionBottomLeft:
02794                 moveResizeGeom =  QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ;
02795                 break;
02796             case PositionTopRight:
02797                 moveResizeGeom =  QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
02798                 break;
02799             // The side ones can't be copied exactly - if aspect ratios are specified, both dimensions may change.
02800             // Therefore grow to the right/bottom if needed.
02801             // TODO it should probably obey gravity rather than always using right/bottom ?
02802             case PositionTop:
02803                 moveResizeGeom =  QRect( QPoint( orig.left(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
02804                 break;
02805             case PositionBottom:
02806                 moveResizeGeom =  QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ;
02807                 break;
02808             case PositionLeft:
02809                 moveResizeGeom =  QRect( QPoint( topleft.x(), orig.top() ), QPoint( orig.right(), bottomright.y()));
02810                 break;
02811             case PositionRight:
02812                 moveResizeGeom =  QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ;
02813                 break;
02814             case PositionCenter:
02815             default:
02816                 abort();
02817                 break;
02818             }
02819         if( moveResizeGeom.size() != previousMoveResizeGeom.size())
02820             update = true;
02821         }
02822     else if( isMove())
02823         {
02824         assert( mode == PositionCenter );
02825         if( !isMovable() ) // isMovableAcrossScreens() must have been true to get here
02826             { // Special moving of maximized windows on Xinerama screens
02827             int screen = workspace()->screenNumber( globalPos );
02828             moveResizeGeom = workspace()->clientArea( MaximizeArea, screen, 0 );
02829             }
02830         else
02831             {
02832             // first move, then snap, then check bounds
02833             moveResizeGeom.moveTopLeft( topleft );
02834             moveResizeGeom.moveTopLeft( workspace()->adjustClientPosition( this, moveResizeGeom.topLeft(),
02835                                                                            unrestrictedMoveResize ));
02836             // NOTE: This is duped in checkUnrestrictedMoveResize().
02837             if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
02838                 moveResizeGeom.moveBottom( desktopArea.top() + titlebar_marge - 1 );
02839             // no need to check top_marge, titlebar_marge already handles it
02840             if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
02841                 moveResizeGeom.moveTop( desktopArea.bottom() - bottom_marge );
02842             if( moveResizeGeom.right() < desktopArea.left() + left_marge )
02843                 moveResizeGeom.moveRight( desktopArea.left() + left_marge );
02844             if( moveResizeGeom.left() > desktopArea.right() - right_marge )
02845                 moveResizeGeom.moveLeft(desktopArea.right() - right_marge );
02846             }
02847         if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft())
02848             update = true;
02849         }
02850     else
02851         abort();
02852 
02853     if( isResize())
02854         {
02855         if( sync_timeout != NULL )
02856             {
02857             sync_resize_pending = true;
02858             return;
02859             }
02860         }
02861 
02862     if( update )
02863         performMoveResize();
02864     if ( isMove() )
02865         workspace()->checkElectricBorder(globalPos, xTime());
02866     }
02867 
02868 void Client::performMoveResize()
02869     {
02870 #ifdef HAVE_XSYNC
02871     if( isResize() && sync_counter != None )
02872         {
02873         sync_timeout = new QTimer( this );
02874         connect( sync_timeout, SIGNAL( timeout()), SLOT( syncTimeout()));
02875         sync_timeout->setSingleShot( true );
02876         sync_timeout->start( 500 );
02877         sendSyncRequest();
02878         }
02879 #endif
02880     sync_resize_pending = false;
02881     if( rules()->checkMoveResizeMode
02882         ( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque )
02883         {
02884         setGeometry( moveResizeGeom );
02885         positionGeometryTip();
02886         }
02887     else if( rules()->checkMoveResizeMode
02888         ( isResize() ? options->resizeMode : options->moveMode ) == Options::Transparent )
02889         {
02890         clearbound();  // it's necessary to move the geometry tip when there's no outline
02891         positionGeometryTip(); // shown, otherwise it would cause repaint problems in case
02892         drawbound( moveResizeGeom ); // they overlap; the paint event will come after this,
02893         }                               // so the geometry tip will be painted above the outline
02894     if( effects )
02895         static_cast<EffectsHandlerImpl*>(effects)->windowUserMovedResized( effectWindow(), false, false );
02896     }
02897 
02898 void Client::syncTimeout()
02899     {
02900     sync_timeout->deleteLater();
02901     sync_timeout = NULL;
02902     if( sync_resize_pending )
02903         performMoveResize();
02904     }
02905 
02906 } // namespace

KWin

Skip menu "KWin"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal