add more spacing
[personal-kdebase.git] / workspace / kwin / geometry.cpp
bloba8d92229956ef98580dc2e99866e914ac8126b46
1 /********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
5 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *********************************************************************/
24 This file contains things relevant to geometry, i.e. workspace size,
25 window positions and window sizes.
29 #include "client.h"
30 #include "workspace.h"
32 #include <kapplication.h>
33 #include <kglobal.h>
34 #include <QPainter>
35 #include <kwindowsystem.h>
37 #include "placement.h"
38 #include "notifications.h"
39 #include "geometrytip.h"
40 #include "rules.h"
41 #include "effects.h"
42 #include <QX11Info>
44 #include <kephal/screens.h>
46 namespace KWin
49 //********************************************
50 // Workspace
51 //********************************************
53 /*!
54 Resizes the workspace after an XRANDR screen size change
56 void Workspace::desktopResized()
58 QRect geom = Kephal::ScreenUtils::desktopGeometry();
59 NETSize desktop_geometry;
60 desktop_geometry.width = geom.width();
61 desktop_geometry.height = geom.height();
62 rootInfo->setDesktopGeometry( -1, desktop_geometry );
64 updateClientArea();
65 destroyElectricBorders();
66 updateElectricBorders();
67 if( compositing() )
69 finishCompositing();
70 QTimer::singleShot( 0, this, SLOT( setupCompositing() ) );
74 /*!
75 Updates the current client areas according to the current clients.
77 If the area changes or force is true, the new areas are propagated to the world.
79 The client area is the area that is available for clients (that
80 which is not taken by windows like panels, the top-of-screen menu
81 etc).
83 \sa clientArea()
86 void Workspace::updateClientArea( bool force )
88 int nscreens = Kephal::ScreenUtils::numScreens();
89 kDebug(1212) << "screens: " << nscreens << "desktops: " << numberOfDesktops();
90 QVector< QRect > new_wareas( numberOfDesktops() + 1 );
91 QVector< QVector< QRect > > new_sareas( numberOfDesktops() + 1 );
92 QVector< QRect > screens( nscreens );
93 QRect desktopArea = Kephal::ScreenUtils::desktopGeometry();
94 for( int iS = 0;
95 iS < nscreens;
96 iS ++ )
98 screens [iS] = Kephal::ScreenUtils::screenGeometry( iS );
100 for( int i = 1;
101 i <= numberOfDesktops();
102 ++i )
104 new_wareas[ i ] = desktopArea;
105 new_sareas[ i ].resize( nscreens );
106 for( int iS = 0;
107 iS < nscreens;
108 iS ++ )
109 new_sareas[ i ][ iS ] = screens[ iS ];
111 for ( ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it)
113 if( !(*it)->hasStrut())
114 continue;
115 QRect r = (*it)->adjustedClientArea( desktopArea, desktopArea );
116 if( (*it)->isOnAllDesktops())
118 for( int i = 1;
119 i <= numberOfDesktops();
120 ++i )
122 new_wareas[ i ] = new_wareas[ i ].intersected( r );
123 for( int iS = 0;
124 iS < nscreens;
125 iS ++ )
127 new_sareas[ i ][ iS ] = new_sareas[ i ][ iS ].intersected(
128 (*it)->adjustedClientArea( desktopArea, screens[ iS ] ));
132 else
134 new_wareas[ (*it)->desktop() ] = new_wareas[ (*it)->desktop() ].intersected( r );
135 for( int iS = 0;
136 iS < nscreens;
137 iS ++ )
139 // kDebug (1212) << "adjusting new_sarea: " << screens[ iS ];
140 new_sareas[ (*it)->desktop() ][ iS ]
141 = new_sareas[ (*it)->desktop() ][ iS ].intersected(
142 (*it)->adjustedClientArea( desktopArea, screens[ iS ] ));
146 #if 0
147 for( int i = 1;
148 i <= numberOfDesktops();
149 ++i )
151 for( int iS = 0;
152 iS < nscreens;
153 iS ++ )
154 kDebug (1212) << "new_sarea: " << new_sareas[ i ][ iS ];
156 #endif
157 // TODO topmenu update for screenarea changes?
158 if( topmenu_space != NULL )
160 QRect topmenu_area = desktopArea;
161 topmenu_area.setTop( topMenuHeight());
162 for( int i = 1;
163 i <= numberOfDesktops();
164 ++i )
165 new_wareas[ i ] = new_wareas[ i ].intersected( topmenu_area );
168 bool changed = force;
170 if(screenarea.isEmpty())
171 changed = true;
173 for( int i = 1;
174 !changed && i <= numberOfDesktops();
175 ++i )
177 if( workarea[ i ] != new_wareas[ i ] )
178 changed = true;
179 if( screenarea[ i ].size() != new_sareas[ i ].size())
180 changed = true;
181 for( int iS = 0;
182 !changed && iS < nscreens;
183 iS ++ )
184 if (new_sareas[ i ][ iS ] != screenarea [ i ][ iS ])
185 changed = true;
188 if ( changed )
190 workarea = new_wareas;
191 screenarea = new_sareas;
192 NETRect r;
193 for( int i = 1; i <= numberOfDesktops(); i++)
195 r.pos.x = workarea[ i ].x();
196 r.pos.y = workarea[ i ].y();
197 r.size.width = workarea[ i ].width();
198 r.size.height = workarea[ i ].height();
199 rootInfo->setWorkArea( i, r );
202 updateTopMenuGeometry();
203 for( ClientList::ConstIterator it = clients.constBegin();
204 it != clients.constEnd();
205 ++it)
206 (*it)->checkWorkspacePosition();
207 for( ClientList::ConstIterator it = desktops.constBegin();
208 it != desktops.constEnd();
209 ++it)
210 (*it)->checkWorkspacePosition();
213 kDebug(1212) << "Done.";
216 void Workspace::updateClientArea()
218 updateClientArea( false );
223 returns the area available for clients. This is the desktop
224 geometry minus windows on the dock. Placement algorithms should
225 refer to this rather than geometry().
227 \sa geometry()
229 QRect Workspace::clientArea( clientAreaOption opt, int screen, int desktop ) const
231 if( desktop == NETWinInfo::OnAllDesktops || desktop == 0 )
232 desktop = currentDesktop();
233 if( screen == -1 )
234 screen = activeScreen();
236 QRect sarea = (!screenarea.isEmpty()
237 && screen < screenarea[ desktop ].size()) // screens may be missing during KWin initialization or screen config changes
238 ? screenarea[ desktop ][ screen ]
239 : Kephal::ScreenUtils::screenGeometry( screen );
240 QRect warea = workarea[ desktop ].isNull()
241 ? Kephal::ScreenUtils::desktopGeometry()
242 : workarea[ desktop ];
243 switch (opt)
245 case MaximizeArea:
246 if (options->xineramaMaximizeEnabled)
247 return sarea;
248 else
249 return warea;
250 case MaximizeFullArea:
251 if (options->xineramaMaximizeEnabled)
252 return Kephal::ScreenUtils::screenGeometry( screen );
253 else
254 return Kephal::ScreenUtils::desktopGeometry();
255 case FullScreenArea:
256 if (options->xineramaFullscreenEnabled)
257 return Kephal::ScreenUtils::screenGeometry( screen );
258 else
259 return Kephal::ScreenUtils::desktopGeometry();
260 case PlacementArea:
261 if (options->xineramaPlacementEnabled)
262 return sarea;
263 else
264 return warea;
265 case MovementArea:
266 if (options->xineramaMovementEnabled)
267 return Kephal::ScreenUtils::screenGeometry( screen );
268 else
269 return Kephal::ScreenUtils::desktopGeometry();
270 case WorkArea:
271 return warea;
272 case FullArea:
273 return Kephal::ScreenUtils::desktopGeometry();
274 case ScreenArea:
275 return Kephal::ScreenUtils::screenGeometry( screen );
277 abort();
280 QRect Workspace::clientArea( clientAreaOption opt, const QPoint& p, int desktop ) const
282 int screen = Kephal::ScreenUtils::screenId( p );
283 return clientArea( opt, screen, desktop );
286 QRect Workspace::clientArea( clientAreaOption opt, const Client* c ) const
288 return clientArea( opt, c->geometry().center(), c->desktop());
293 Client \a c is moved around to position \a pos. This gives the
294 workspace the opportunity to interveniate and to implement
295 snap-to-windows functionality.
297 QPoint Workspace::adjustClientPosition( Client* c, QPoint pos, bool unrestricted )
299 //CT 16mar98, 27May98 - magics: BorderSnapZone, WindowSnapZone
300 //CT adapted for kwin on 25Nov1999
301 //aleXXX 02Nov2000 added second snapping mode
302 if (options->windowSnapZone || options->borderSnapZone || options->centerSnapZone )
304 const bool sOWO=options->snapOnlyWhenOverlapping;
305 const QRect maxRect = clientArea(MovementArea, pos+c->rect().center(), c->desktop());
306 const int xmin = maxRect.left();
307 const int xmax = maxRect.right()+1; //desk size
308 const int ymin = maxRect.top();
309 const int ymax = maxRect.bottom()+1;
311 const int cx(pos.x());
312 const int cy(pos.y());
313 const int cw(c->width());
314 const int ch(c->height());
315 const int rx(cx+cw);
316 const int ry(cy+ch); //these don't change
318 int nx(cx), ny(cy); //buffers
319 int deltaX(xmax);
320 int deltaY(ymax); //minimum distance to other clients
322 int lx, ly, lrx, lry; //coords and size for the comparison client, l
324 // border snap
325 int snap = options->borderSnapZone; //snap trigger
326 if (snap)
328 if ((sOWO?(cx<xmin):true) && (qAbs(xmin-cx)<snap))
330 deltaX = xmin-cx;
331 nx = xmin;
333 if ((sOWO?(rx>xmax):true) && (qAbs(rx-xmax)<snap) && (qAbs(xmax-rx) < deltaX))
335 deltaX = rx-xmax;
336 nx = xmax - cw;
339 if ((sOWO?(cy<ymin):true) && (qAbs(ymin-cy)<snap))
341 deltaY = ymin-cy;
342 ny = ymin;
344 if ((sOWO?(ry>ymax):true) && (qAbs(ry-ymax)<snap) && (qAbs(ymax-ry) < deltaY))
346 deltaY =ry-ymax;
347 ny = ymax - ch;
351 // windows snap
352 snap = options->windowSnapZone;
353 if (snap)
355 QList<Client *>::ConstIterator l;
356 for (l = clients.constBegin();l != clients.constEnd();++l )
358 if ((*l)->isOnDesktop(currentDesktop()) &&
359 !(*l)->isMinimized()
360 && (*l) != c )
362 lx = (*l)->x();
363 ly = (*l)->y();
364 lrx = lx + (*l)->width();
365 lry = ly + (*l)->height();
367 if ( (( cy <= lry ) && ( cy >= ly )) ||
368 (( ry >= ly ) && ( ry <= lry )) ||
369 (( cy <= ly ) && ( ry >= lry )) )
371 if ((sOWO?(cx<lrx):true) && (qAbs(lrx-cx)<snap) && ( qAbs(lrx -cx) < deltaX) )
373 deltaX = qAbs( lrx - cx );
374 nx = lrx;
376 if ((sOWO?(rx>lx):true) && (qAbs(rx-lx)<snap) && ( qAbs( rx - lx )<deltaX) )
378 deltaX = qAbs(rx - lx);
379 nx = lx - cw;
383 if ( (( cx <= lrx ) && ( cx >= lx )) ||
384 (( rx >= lx ) && ( rx <= lrx )) ||
385 (( cx <= lx ) && ( rx >= lrx )) )
387 if ((sOWO?(cy<lry):true) && (qAbs(lry-cy)<snap) && (qAbs( lry -cy ) < deltaY))
389 deltaY = qAbs( lry - cy );
390 ny = lry;
392 //if ( (qAbs( ry-ly ) < snap) && (qAbs( ry - ly ) < deltaY ))
393 if ((sOWO?(ry>ly):true) && (qAbs(ry-ly)<snap) && (qAbs( ry - ly ) < deltaY ))
395 deltaY = qAbs( ry - ly );
396 ny = ly - ch;
400 // Corner snapping
401 if( nx == lrx || nx+cw == lx )
403 if ((sOWO?(ry>lry):true) && (qAbs(lry-ry)<snap) && (qAbs(lry-ry) < deltaY))
405 deltaY = qAbs( lry - ry );
406 ny = lry - ch;
408 if ((sOWO?(cy<ly):true) && (qAbs(cy-ly)<snap) && (qAbs(cy-ly) < deltaY))
410 deltaY = qAbs( cy - ly );
411 ny = ly;
414 if( ny == lry || ny+ch == ly )
416 if ((sOWO?(rx>lrx):true) && (qAbs(lrx-rx)<snap) && (qAbs(lrx-rx) < deltaX))
418 deltaX = qAbs( lrx - rx );
419 nx = lrx - cw;
421 if ((sOWO?(cx<lx):true) && (qAbs(cx-lx)<snap) && (qAbs(cx-lx) < deltaX))
423 deltaX = qAbs( cx - lx );
424 nx = lx;
431 // center snap
432 snap = options->centerSnapZone; //snap trigger
433 if (snap)
435 int diffX = qAbs( (xmin + xmax)/2 - (cx + cw/2) );
436 int diffY = qAbs( (ymin + ymax)/2 - (cy + ch/2) );
437 if (diffX < snap && diffY < snap && diffX < deltaX && diffY < deltaY)
438 { // Snap to center of screen
439 deltaX = diffX;
440 deltaY = diffY;
441 nx = (xmin + xmax)/2 - cw/2;
442 ny = (ymin + ymax)/2 - ch/2;
444 else if ( options->borderSnapZone )
445 { // Enhance border snap
446 if( ( nx == xmin || nx == xmax - cw ) && diffY < snap && diffY < deltaY)
447 { // Snap to vertical center on screen edge
448 deltaY = diffY;
449 ny = (ymin + ymax)/2 - ch/2;
451 else if ( (( unrestricted ? ny == ymin : ny <= ymin) || ny == ymax - ch ) &&
452 diffX < snap && diffX < deltaX)
453 { // Snap to horizontal center on screen edge
454 deltaX = diffX;
455 nx = (xmin + xmax)/2 - cw/2;
460 pos = QPoint(nx, ny);
462 return pos;
465 QRect Workspace::adjustClientSize( Client* c, QRect moveResizeGeom, int mode )
467 //adapted from adjustClientPosition on 29May2004
468 //this function is called when resizing a window and will modify
469 //the new dimensions to snap to other windows/borders if appropriate
470 if ( options->windowSnapZone || options->borderSnapZone ) // || options->centerSnapZone )
472 const bool sOWO=options->snapOnlyWhenOverlapping;
474 const QRect maxRect = clientArea(MovementArea, c->rect().center(), c->desktop());
475 const int xmin = maxRect.left();
476 const int xmax = maxRect.right(); //desk size
477 const int ymin = maxRect.top();
478 const int ymax = maxRect.bottom();
480 const int cx(moveResizeGeom.left());
481 const int cy(moveResizeGeom.top());
482 const int rx(moveResizeGeom.right());
483 const int ry(moveResizeGeom.bottom());
485 int newcx(cx), newcy(cy); //buffers
486 int newrx(rx), newry(ry);
487 int deltaX(xmax);
488 int deltaY(ymax); //minimum distance to other clients
490 int lx, ly, lrx, lry; //coords and size for the comparison client, l
492 // border snap
493 int snap = options->borderSnapZone; //snap trigger
494 if (snap)
496 deltaX = int(snap);
497 deltaY = int(snap);
499 #define SNAP_BORDER_TOP \
500 if ((sOWO?(newcy<ymin):true) && (qAbs(ymin-newcy)<deltaY)) \
502 deltaY = qAbs(ymin-newcy); \
503 newcy = ymin; \
506 #define SNAP_BORDER_BOTTOM \
507 if ((sOWO?(newry>ymax):true) && (qAbs(ymax-newry)<deltaY)) \
509 deltaY = qAbs(ymax-newcy); \
510 newry = ymax; \
513 #define SNAP_BORDER_LEFT \
514 if ((sOWO?(newcx<xmin):true) && (qAbs(xmin-newcx)<deltaX)) \
516 deltaX = qAbs(xmin-newcx); \
517 newcx = xmin; \
520 #define SNAP_BORDER_RIGHT \
521 if ((sOWO?(newrx>xmax):true) && (qAbs(xmax-newrx)<deltaX)) \
523 deltaX = qAbs(xmax-newrx); \
524 newrx = xmax; \
526 switch ( mode )
528 case PositionBottomRight:
529 SNAP_BORDER_BOTTOM
530 SNAP_BORDER_RIGHT
531 break;
532 case PositionRight:
533 SNAP_BORDER_RIGHT
534 break;
535 case PositionBottom:
536 SNAP_BORDER_BOTTOM
537 break;
538 case PositionTopLeft:
539 SNAP_BORDER_TOP
540 SNAP_BORDER_LEFT
541 break;
542 case PositionLeft:
543 SNAP_BORDER_LEFT
544 break;
545 case PositionTop:
546 SNAP_BORDER_TOP
547 break;
548 case PositionTopRight:
549 SNAP_BORDER_TOP
550 SNAP_BORDER_RIGHT
551 break;
552 case PositionBottomLeft:
553 SNAP_BORDER_BOTTOM
554 SNAP_BORDER_LEFT
555 break;
556 default:
557 abort();
558 break;
564 // windows snap
565 snap = options->windowSnapZone;
566 if (snap)
568 deltaX = int(snap);
569 deltaY = int(snap);
570 QList<Client *>::ConstIterator l;
571 for (l = clients.constBegin();l != clients.constEnd();++l )
573 if ((*l)->isOnDesktop(currentDesktop()) &&
574 !(*l)->isMinimized()
575 && (*l) != c )
577 lx = (*l)->x()-1;
578 ly = (*l)->y()-1;
579 lrx =(*l)->x() + (*l)->width();
580 lry =(*l)->y() + (*l)->height();
582 #define WITHIN_HEIGHT ((( newcy <= lry ) && ( newcy >= ly )) || \
583 (( newry >= ly ) && ( newry <= lry )) || \
584 (( newcy <= ly ) && ( newry >= lry )) )
586 #define WITHIN_WIDTH ( (( cx <= lrx ) && ( cx >= lx )) || \
587 (( rx >= lx ) && ( rx <= lrx )) || \
588 (( cx <= lx ) && ( rx >= lrx )) )
590 #define SNAP_WINDOW_TOP if ( (sOWO?(newcy<lry):true) \
591 && WITHIN_WIDTH \
592 && (qAbs( lry - newcy ) < deltaY) ) { \
593 deltaY = qAbs( lry - newcy ); \
594 newcy=lry; \
597 #define SNAP_WINDOW_BOTTOM if ( (sOWO?(newry>ly):true) \
598 && WITHIN_WIDTH \
599 && (qAbs( ly - newry ) < deltaY) ) { \
600 deltaY = qAbs( ly - newry ); \
601 newry=ly; \
604 #define SNAP_WINDOW_LEFT if ( (sOWO?(newcx<lrx):true) \
605 && WITHIN_HEIGHT \
606 && (qAbs( lrx - newcx ) < deltaX)) { \
607 deltaX = qAbs( lrx - newcx ); \
608 newcx=lrx; \
611 #define SNAP_WINDOW_RIGHT if ( (sOWO?(newrx>lx):true) \
612 && WITHIN_HEIGHT \
613 && (qAbs( lx - newrx ) < deltaX)) \
615 deltaX = qAbs( lx - newrx ); \
616 newrx=lx; \
619 #define SNAP_WINDOW_C_TOP if ( (sOWO?(newcy<ly):true) \
620 && (newcx == lrx || newrx == lx) \
621 && qAbs(ly-newcy) < deltaY ) { \
622 deltaY = qAbs( ly - newcy + 1 ); \
623 newcy = ly + 1; \
626 #define SNAP_WINDOW_C_BOTTOM if ( (sOWO?(newry>lry):true) \
627 && (newcx == lrx || newrx == lx) \
628 && qAbs(lry-newry) < deltaY ) { \
629 deltaY = qAbs( lry - newry - 1 ); \
630 newry = lry - 1; \
633 #define SNAP_WINDOW_C_LEFT if ( (sOWO?(newcx<lx):true) \
634 && (newcy == lry || newry == ly) \
635 && qAbs(lx-newcx) < deltaX ) { \
636 deltaX = qAbs( lx - newcx + 1 ); \
637 newcx = lx + 1; \
640 #define SNAP_WINDOW_C_RIGHT if ( (sOWO?(newrx>lrx):true) \
641 && (newcy == lry || newry == ly) \
642 && qAbs(lrx-newrx) < deltaX ) { \
643 deltaX = qAbs( lrx - newrx - 1 ); \
644 newrx = lrx - 1; \
647 switch ( mode )
649 case PositionBottomRight:
650 SNAP_WINDOW_BOTTOM
651 SNAP_WINDOW_RIGHT
652 SNAP_WINDOW_C_BOTTOM
653 SNAP_WINDOW_C_RIGHT
654 break;
655 case PositionRight:
656 SNAP_WINDOW_RIGHT
657 SNAP_WINDOW_C_RIGHT
658 break;
659 case PositionBottom:
660 SNAP_WINDOW_BOTTOM
661 SNAP_WINDOW_C_BOTTOM
662 break;
663 case PositionTopLeft:
664 SNAP_WINDOW_TOP
665 SNAP_WINDOW_LEFT
666 SNAP_WINDOW_C_TOP
667 SNAP_WINDOW_C_LEFT
668 break;
669 case PositionLeft:
670 SNAP_WINDOW_LEFT
671 SNAP_WINDOW_C_LEFT
672 break;
673 case PositionTop:
674 SNAP_WINDOW_TOP
675 SNAP_WINDOW_C_TOP
676 break;
677 case PositionTopRight:
678 SNAP_WINDOW_TOP
679 SNAP_WINDOW_RIGHT
680 SNAP_WINDOW_C_TOP
681 SNAP_WINDOW_C_RIGHT
682 break;
683 case PositionBottomLeft:
684 SNAP_WINDOW_BOTTOM
685 SNAP_WINDOW_LEFT
686 SNAP_WINDOW_C_BOTTOM
687 SNAP_WINDOW_C_LEFT
688 break;
689 default:
690 abort();
691 break;
697 // center snap
698 //snap = options->centerSnapZone;
699 //if (snap)
700 // {
701 // // Don't resize snap to center as it interferes too much
702 // // There are two ways of implementing this if wanted:
703 // // 1) Snap only to the same points that the move snap does, and
704 // // 2) Snap to the horizontal and vertical center lines of the screen
705 // }
707 moveResizeGeom = QRect(QPoint(newcx, newcy), QPoint(newrx, newry));
709 return moveResizeGeom;
713 Marks the client as being moved around by the user.
715 void Workspace::setClientIsMoving( Client *c )
717 Q_ASSERT(!c || !movingClient); // Catch attempts to move a second
718 // window while still moving the first one.
719 movingClient = c;
720 if (movingClient)
721 ++block_focus;
722 else
723 --block_focus;
727 Cascades all clients on the current desktop
729 void Workspace::cascadeDesktop()
731 // TODO XINERAMA this probably is not right for xinerama
732 Q_ASSERT( block_stacking_updates == 0 );
733 ClientList::ConstIterator it(stackingOrder().begin());
734 initPositioning->reinitCascading( currentDesktop());
735 QRect area = clientArea( PlacementArea, QPoint( 0, 0 ), currentDesktop());
736 for (; it != stackingOrder().end(); ++it)
738 if((!(*it)->isOnDesktop(currentDesktop())) ||
739 ((*it)->isMinimized()) ||
740 ((*it)->isOnAllDesktops()) ||
741 (!(*it)->isMovable()) )
742 continue;
743 initPositioning->placeCascaded(*it, area);
748 Unclutters the current desktop by smart-placing all clients
749 again.
751 void Workspace::unclutterDesktop()
753 for ( int i = clients.size() - 1; i>=0; i-- )
755 if( ( !clients.at( i )->isOnDesktop( currentDesktop() ) ) ||
756 (clients.at( i )->isMinimized()) ||
757 (clients.at( i )->isOnAllDesktops()) ||
758 (!clients.at( i )->isMovable()) )
759 continue;
760 initPositioning->placeSmart(clients.at( i ), QRect());
765 void Workspace::updateTopMenuGeometry( Client* c )
767 if( !managingTopMenus())
768 return;
769 if( c != NULL )
771 XEvent ev;
772 ev.xclient.display = display();
773 ev.xclient.type = ClientMessage;
774 ev.xclient.window = c->window();
775 static Atom msg_type_atom = XInternAtom( display(), "_KDE_TOPMENU_MINSIZE", False );
776 ev.xclient.message_type = msg_type_atom;
777 ev.xclient.format = 32;
778 ev.xclient.data.l[0] = xTime();
779 ev.xclient.data.l[1] = topmenu_space->width();
780 ev.xclient.data.l[2] = topmenu_space->height();
781 ev.xclient.data.l[3] = 0;
782 ev.xclient.data.l[4] = 0;
783 XSendEvent( display(), c->window(), False, NoEventMask, &ev );
784 KWindowSystem::setStrut( c->window(), 0, 0, topmenu_height, 0 ); // so that kicker etc. know
785 c->checkWorkspacePosition();
786 return;
788 // c == NULL - update all, including topmenu_space
789 QRect area;
790 area = clientArea( MaximizeFullArea, QPoint( 0, 0 ), 1 ); // HACK desktop ?
791 area.setHeight( topMenuHeight());
792 topmenu_space->setGeometry( area );
793 for( ClientList::ConstIterator it = topmenus.constBegin();
794 it != topmenus.constEnd();
795 ++it )
796 updateTopMenuGeometry( *it );
799 //********************************************
800 // Client
801 //********************************************
804 void Client::keepInArea( QRect area, bool partial )
806 if( partial )
808 // increase the area so that can have only 100 pixels in the area
809 area.setLeft( qMin( area.left() - width() + 100, area.left()));
810 area.setTop( qMin( area.top() - height() + 100, area.top()));
811 area.setRight( qMax( area.right() + width() - 100, area.right()));
812 area.setBottom( qMax( area.bottom() + height() - 100, area.bottom()));
814 if( !partial )
815 { // resize to fit into area
816 if( area.width() < width() || area.height() < height())
817 resizeWithChecks( qMin( area.width(), width()), qMin( area.height(), height()));
819 if ( geometry().right() > area.right() && width() < area.width() )
820 move( area.right() - width(), y() );
821 if ( geometry().bottom() > area.bottom() && height() < area.height() )
822 move( x(), area.bottom() - height() );
823 if( !area.contains( geometry().topLeft() ))
825 int tx = x();
826 int ty = y();
827 if ( tx < area.x() )
828 tx = area.x();
829 if ( ty < area.y() )
830 ty = area.y();
831 move( tx, ty );
836 Returns \a area with the client's strut taken into account.
838 Used from Workspace in updateClientArea.
840 // TODO move to Workspace?
842 QRect Client::adjustedClientArea( const QRect &desktopArea, const QRect& area ) const
844 QRect r = area;
845 // topmenu area is reserved in updateClientArea()
846 if( isTopMenu())
847 return r;
848 NETExtendedStrut str = strut();
849 QRect stareaL = QRect(
851 str . left_start,
852 str . left_width,
853 str . left_end - str . left_start + 1 );
854 QRect stareaR = QRect (
855 desktopArea . right () - str . right_width + 1,
856 str . right_start,
857 str . right_width,
858 str . right_end - str . right_start + 1 );
859 QRect stareaT = QRect (
860 str . top_start,
862 str . top_end - str . top_start + 1,
863 str . top_width);
864 QRect stareaB = QRect (
865 str . bottom_start,
866 desktopArea . bottom () - str . bottom_width + 1,
867 str . bottom_end - str . bottom_start + 1,
868 str . bottom_width);
870 QRect screenarea = workspace()->clientArea( ScreenArea, this );
871 // HACK: workarea handling is not xinerama aware, so if this strut
872 // reserves place at a xinerama edge that's inside the virtual screen,
873 // ignore the strut for workspace setting.
874 if( area == Kephal::ScreenUtils::desktopGeometry())
876 if( stareaL.left() < screenarea.left())
877 stareaL = QRect();
878 if( stareaR.right() > screenarea.right())
879 stareaR = QRect();
880 if( stareaT.top() < screenarea.top())
881 stareaT = QRect();
882 if( stareaB.bottom() < screenarea.bottom())
883 stareaB = QRect();
885 // Handle struts at xinerama edges that are inside the virtual screen.
886 // They're given in virtual screen coordinates, make them affect only
887 // their xinerama screen.
888 stareaL.setLeft( qMax( stareaL.left(), screenarea.left()));
889 stareaR.setRight( qMin( stareaR.right(), screenarea.right()));
890 stareaT.setTop( qMax( stareaT.top(), screenarea.top()));
891 stareaB.setBottom( qMin( stareaB.bottom(), screenarea.bottom()));
893 if (stareaL . intersects (area)) {
894 // kDebug (1212) << "Moving left of: " << r << " to " << stareaL.right() + 1;
895 r . setLeft( stareaL . right() + 1 );
897 if (stareaR . intersects (area)) {
898 // kDebug (1212) << "Moving right of: " << r << " to " << stareaR.left() - 1;
899 r . setRight( stareaR . left() - 1 );
901 if (stareaT . intersects (area)) {
902 // kDebug (1212) << "Moving top of: " << r << " to " << stareaT.bottom() + 1;
903 r . setTop( stareaT . bottom() + 1 );
905 if (stareaB . intersects (area)) {
906 // kDebug (1212) << "Moving bottom of: " << r << " to " << stareaB.top() - 1;
907 r . setBottom( stareaB . top() - 1 );
909 return r;
912 NETExtendedStrut Client::strut() const
914 NETExtendedStrut ext = info->extendedStrut();
915 NETStrut str = info->strut();
916 if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0
917 && ( str.left != 0 || str.right != 0 || str.top != 0 || str.bottom != 0 ))
919 // build extended from simple
920 if( str.left != 0 )
922 ext.left_width = str.left;
923 ext.left_start = 0;
924 ext.left_end = displayHeight();
926 if( str.right != 0 )
928 ext.right_width = str.right;
929 ext.right_start = 0;
930 ext.right_end = displayHeight();
932 if( str.top != 0 )
934 ext.top_width = str.top;
935 ext.top_start = 0;
936 ext.top_end = displayWidth();
938 if( str.bottom != 0 )
940 ext.bottom_width = str.bottom;
941 ext.bottom_start = 0;
942 ext.bottom_end = displayWidth();
945 return ext;
948 bool Client::hasStrut() const
950 NETExtendedStrut ext = strut();
951 if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0 )
952 return false;
953 return true;
957 // updates differences to workarea edges for all directions
958 void Client::updateWorkareaDiffs()
960 QRect area = workspace()->clientArea( WorkArea, this );
961 QRect geom = geometry();
962 workarea_diff_x = computeWorkareaDiff( geom.left(), geom.right(), area.left(), area.right());
963 workarea_diff_y = computeWorkareaDiff( geom.top(), geom.bottom(), area.top(), area.bottom());
966 // If the client was inside workarea in the x direction, and if it was close to the left/right
967 // edge, return the distance from the left/right edge (negative for left, positive for right)
968 // INT_MIN means 'not inside workarea', INT_MAX means 'not near edge'.
969 // In order to recognize 'at the left workarea edge' from 'at the right workarea edge'
970 // (i.e. negative vs positive zero), the distances are one larger in absolute value than they
971 // really are (i.e. 5 pixels from the left edge is -6, not -5). A bit hacky, but I'm lazy
972 // to rewrite it just to make it nicer. If this will ever get touched again, perhaps then.
973 // the y direction is done the same, just the values will be rotated: top->left, bottom->right
974 int Client::computeWorkareaDiff( int left, int right, int a_left, int a_right )
976 int left_diff = left - a_left;
977 int right_diff = a_right - right;
978 if( left_diff < 0 || right_diff < 0 )
979 return INT_MIN;
980 else // fully inside workarea in this direction direction
982 // max distance from edge where it's still considered to be close and is kept at that distance
983 int max_diff = ( a_right - a_left ) / 10;
984 if( left_diff < right_diff )
985 return left_diff < max_diff ? -left_diff - 1 : INT_MAX;
986 else if( left_diff > right_diff )
987 return right_diff < max_diff ? right_diff + 1 : INT_MAX;
988 return INT_MAX; // not close to workarea edge
992 void Client::checkWorkspacePosition()
994 if( isDesktop())
995 return;
996 if( isFullScreen())
998 QRect area = workspace()->clientArea( FullScreenArea, this );
999 if( geometry() != area )
1000 setGeometry( area );
1001 return;
1003 if( isDock())
1004 return;
1005 if( isTopMenu())
1007 if( workspace()->managingTopMenus())
1009 QRect area;
1010 ClientList mainclients = mainClients();
1011 if( mainclients.count() == 1 )
1012 area = workspace()->clientArea( MaximizeFullArea, mainclients.first());
1013 else
1014 area = workspace()->clientArea( MaximizeFullArea, QPoint( 0, 0 ), desktop());
1015 area.setHeight( workspace()->topMenuHeight());
1016 // kDebug(1212) << "TOPMENU size adjust: " << area << ":" << this;
1017 setGeometry( area );
1019 return;
1022 if( maximizeMode() != MaximizeRestore )
1023 // TODO update geom_restore?
1024 changeMaximize( false, false, true ); // adjust size
1026 if( !isShade()) // TODO
1028 int old_diff_x = workarea_diff_x;
1029 int old_diff_y = workarea_diff_y;
1030 updateWorkareaDiffs();
1032 // this can be true only if this window was mapped before KWin
1033 // was started - in such case, don't adjust position to workarea,
1034 // because the window already had its position, and if a window
1035 // with a strut altering the workarea would be managed in initialization
1036 // after this one, this window would be moved
1037 if( workspace()->initializing())
1038 return;
1040 QRect area = workspace()->clientArea( WorkArea, this );
1041 QRect new_geom = geometry();
1042 QRect tmp_rect_x( new_geom.left(), 0, new_geom.width(), 0 );
1043 QRect tmp_area_x( area.left(), 0, area.width(), 0 );
1044 checkDirection( workarea_diff_x, old_diff_x, tmp_rect_x, tmp_area_x );
1045 // the x<->y swapping
1046 QRect tmp_rect_y( new_geom.top(), 0, new_geom.height(), 0 );
1047 QRect tmp_area_y( area.top(), 0, area.height(), 0 );
1048 checkDirection( workarea_diff_y, old_diff_y, tmp_rect_y, tmp_area_y );
1049 new_geom = QRect( tmp_rect_x.left(), tmp_rect_y.left(), tmp_rect_x.width(), tmp_rect_y.width());
1050 QRect final_geom( new_geom.topLeft(), adjustedSize( new_geom.size()));
1051 if( final_geom != new_geom ) // size increments, or size restrictions
1052 { // adjusted size differing matters only for right and bottom edge
1053 if( old_diff_x != INT_MAX && old_diff_x > 0 )
1054 final_geom.moveRight( area.right() - ( old_diff_x - 1 ));
1055 if( old_diff_y != INT_MAX && old_diff_y > 0 )
1056 final_geom.moveBottom( area.bottom() - ( old_diff_y - 1 ));
1058 if( final_geom != geometry() )
1059 setGeometry( final_geom );
1060 // updateWorkareaDiffs(); done already by setGeometry()
1064 // Try to be smart about keeping the clients visible.
1065 // If the client was fully inside the workspace before, try to keep
1066 // it still inside the workarea, possibly moving it or making it smaller if possible,
1067 // and try to keep the distance from the nearest workarea edge.
1068 // On the other hand, it it was partially moved outside of the workspace in some direction,
1069 // don't do anything with that direction if it's still at least partially visible. If it's
1070 // not visible anymore at all, make sure it's visible at least partially
1071 // again (not fully, as that could(?) be potentionally annoying) by
1072 // moving it slightly inside the workarea (those '+ 5').
1073 // Again, this is done for the x direction, y direction will be done by x<->y swapping
1074 void Client::checkDirection( int new_diff, int old_diff, QRect& rect, const QRect& area )
1076 if( old_diff != INT_MIN ) // was inside workarea
1078 if( old_diff == INT_MAX ) // was in workarea, but far from edge
1080 if( new_diff == INT_MIN ) // is not anymore fully in workarea
1082 rect.setLeft( area.left());
1083 rect.setRight( area.right());
1085 return;
1087 if( isMovable())
1089 if( old_diff < 0 ) // was in left third, keep distance from left edge
1090 rect.moveLeft( area.left() + ( -old_diff - 1 ));
1091 else // old_diff > 0 // was in right third, keep distance from right edge
1092 rect.moveRight( area.right() - ( old_diff - 1 ));
1094 else if( isResizable())
1096 if( old_diff < 0 )
1097 rect.setLeft( area.left() + ( -old_diff - 1 ) );
1098 else // old_diff > 0
1099 rect.setRight( area.right() - ( old_diff - 1 ));
1101 if( rect.width() > area.width() && isResizable())
1102 rect.setWidth( area.width());
1103 if( isMovable())
1105 if( rect.left() < area.left())
1106 rect.moveLeft( area.left());
1107 else if( rect.right() > area.right())
1108 rect.moveRight( area.right());
1111 if( rect.right() < area.left() + 5 || rect.left() > area.right() - 5 )
1112 { // not visible (almost) at all - try to make it at least partially visible
1113 if( isMovable())
1115 if( rect.left() < area.left() + 5 )
1116 rect.moveRight( area.left() + 5 );
1117 if( rect.right() > area.right() - 5 )
1118 rect.moveLeft( area.right() - 5 );
1124 Adjust the frame size \a frame according to he window's size hints.
1126 QSize Client::adjustedSize( const QSize& frame, Sizemode mode ) const
1128 // first, get the window size for the given frame size s
1130 QSize wsize( frame.width() - ( border_left + border_right ),
1131 frame.height() - ( border_top + border_bottom ));
1132 if( wsize.isEmpty())
1133 wsize = QSize( 1, 1 );
1135 return sizeForClientSize( wsize, mode, false );
1138 // this helper returns proper size even if the window is shaded
1139 // see also the comment in Client::setGeometry()
1140 QSize Client::adjustedSize() const
1142 return sizeForClientSize( clientSize());
1146 Calculate the appropriate frame size for the given client size \a
1147 wsize.
1149 \a wsize is adapted according to the window's size hints (minimum,
1150 maximum and incremental size changes).
1153 QSize Client::sizeForClientSize( const QSize& wsize, Sizemode mode, bool noframe ) const
1155 int w = wsize.width();
1156 int h = wsize.height();
1157 if( w < 1 || h < 1 )
1159 kWarning(1212) << "sizeForClientSize() with empty size!" ;
1160 kWarning(1212) << kBacktrace() ;
1162 if (w<1) w = 1;
1163 if (h<1) h = 1;
1165 // basesize, minsize, maxsize, paspect and resizeinc have all values defined,
1166 // even if they're not set in flags - see getWmNormalHints()
1167 QSize min_size = minSize();
1168 QSize max_size = maxSize();
1169 if( decoration != NULL )
1171 QSize decominsize = decoration->minimumSize();
1172 QSize border_size( border_left + border_right, border_top + border_bottom );
1173 if( border_size.width() > decominsize.width()) // just in case
1174 decominsize.setWidth( border_size.width());
1175 if( border_size.height() > decominsize.height())
1176 decominsize.setHeight( border_size.height());
1177 if( decominsize.width() > min_size.width())
1178 min_size.setWidth( decominsize.width());
1179 if( decominsize.height() > min_size.height())
1180 min_size.setHeight( decominsize.height());
1182 w = qMin( max_size.width(), w );
1183 h = qMin( max_size.height(), h );
1184 w = qMax( min_size.width(), w );
1185 h = qMax( min_size.height(), h );
1187 int w1 = w;
1188 int h1 = h;
1189 int width_inc = xSizeHint.width_inc;
1190 int height_inc = xSizeHint.height_inc;
1191 int basew_inc = xSizeHint.min_width; // see getWmNormalHints()
1192 int baseh_inc = xSizeHint.min_height;
1193 w = int(( w - basew_inc ) / width_inc ) * width_inc + basew_inc;
1194 h = int(( h - baseh_inc ) / height_inc ) * height_inc + baseh_inc;
1195 // code for aspect ratios based on code from FVWM
1197 * The math looks like this:
1199 * minAspectX dwidth maxAspectX
1200 * ---------- <= ------- <= ----------
1201 * minAspectY dheight maxAspectY
1203 * If that is multiplied out, then the width and height are
1204 * invalid in the following situations:
1206 * minAspectX * dheight > minAspectY * dwidth
1207 * maxAspectX * dheight < maxAspectY * dwidth
1210 if( xSizeHint.flags & PAspect )
1212 double min_aspect_w = xSizeHint.min_aspect.x; // use doubles, because the values can be MAX_INT
1213 double min_aspect_h = xSizeHint.min_aspect.y; // and multiplying would go wrong otherwise
1214 double max_aspect_w = xSizeHint.max_aspect.x;
1215 double max_aspect_h = xSizeHint.max_aspect.y;
1216 // According to ICCCM 4.1.2.3 PMinSize should be a fallback for PBaseSize for size increments,
1217 // but not for aspect ratio. Since this code comes from FVWM, handles both at the same time,
1218 // and I have no idea how it works, let's hope nobody relies on that.
1219 w -= xSizeHint.base_width;
1220 h -= xSizeHint.base_height;
1221 int max_width = max_size.width() - xSizeHint.base_width;
1222 int min_width = min_size.width() - xSizeHint.base_width;
1223 int max_height = max_size.height() - xSizeHint.base_height;
1224 int min_height = min_size.height() - xSizeHint.base_height;
1225 #define ASPECT_CHECK_GROW_W \
1226 if( min_aspect_w * h > min_aspect_h * w ) \
1228 int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
1229 if( w + delta <= max_width ) \
1230 w += delta; \
1232 #define ASPECT_CHECK_SHRINK_H_GROW_W \
1233 if( min_aspect_w * h > min_aspect_h * w ) \
1235 int delta = int( h - w * min_aspect_h / min_aspect_w ) / height_inc * height_inc; \
1236 if( h - delta >= min_height ) \
1237 h -= delta; \
1238 else \
1240 int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
1241 if( w + delta <= max_width ) \
1242 w += delta; \
1245 #define ASPECT_CHECK_GROW_H \
1246 if( max_aspect_w * h < max_aspect_h * w ) \
1248 int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
1249 if( h + delta <= max_height ) \
1250 h += delta; \
1252 #define ASPECT_CHECK_SHRINK_W_GROW_H \
1253 if( max_aspect_w * h < max_aspect_h * w ) \
1255 int delta = int( w - max_aspect_w * h / max_aspect_h ) / width_inc * width_inc; \
1256 if( w - delta >= min_width ) \
1257 w -= delta; \
1258 else \
1260 int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
1261 if( h + delta <= max_height ) \
1262 h += delta; \
1265 switch( mode )
1267 case SizemodeAny:
1268 #if 0 // make SizemodeAny equal to SizemodeFixedW - prefer keeping fixed width,
1269 // so that changing aspect ratio to a different value and back keeps the same size (#87298)
1271 ASPECT_CHECK_SHRINK_H_GROW_W
1272 ASPECT_CHECK_SHRINK_W_GROW_H
1273 ASPECT_CHECK_GROW_H
1274 ASPECT_CHECK_GROW_W
1275 break;
1277 #endif
1278 case SizemodeFixedW:
1280 // the checks are order so that attempts to modify height are first
1281 ASPECT_CHECK_GROW_H
1282 ASPECT_CHECK_SHRINK_H_GROW_W
1283 ASPECT_CHECK_SHRINK_W_GROW_H
1284 ASPECT_CHECK_GROW_W
1285 break;
1287 case SizemodeFixedH:
1289 ASPECT_CHECK_GROW_W
1290 ASPECT_CHECK_SHRINK_W_GROW_H
1291 ASPECT_CHECK_SHRINK_H_GROW_W
1292 ASPECT_CHECK_GROW_H
1293 break;
1295 case SizemodeMax:
1297 // first checks that try to shrink
1298 ASPECT_CHECK_SHRINK_H_GROW_W
1299 ASPECT_CHECK_SHRINK_W_GROW_H
1300 ASPECT_CHECK_GROW_W
1301 ASPECT_CHECK_GROW_H
1302 break;
1305 #undef ASPECT_CHECK_SHRINK_H_GROW_W
1306 #undef ASPECT_CHECK_SHRINK_W_GROW_H
1307 #undef ASPECT_CHECK_GROW_W
1308 #undef ASPECT_CHECK_GROW_H
1309 w += xSizeHint.base_width;
1310 h += xSizeHint.base_height;
1312 if( !rules()->checkStrictGeometry( false ))
1314 // disobey increments and aspect when maximized
1315 if( maximizeMode() & MaximizeHorizontal )
1316 w = w1;
1317 if( maximizeMode() & MaximizeVertical )
1318 h = h1;
1321 if( !noframe )
1323 w += border_left + border_right;
1324 h += border_top + border_bottom;
1326 return rules()->checkSize( QSize( w, h ));
1330 Gets the client's normal WM hints and reconfigures itself respectively.
1332 void Client::getWmNormalHints()
1334 long msize;
1335 if (XGetWMNormalHints(display(), window(), &xSizeHint, &msize) == 0 )
1336 xSizeHint.flags = 0;
1337 // set defined values for the fields, even if they're not in flags
1339 if( ! ( xSizeHint.flags & PMinSize ))
1340 xSizeHint.min_width = xSizeHint.min_height = 0;
1341 if( xSizeHint.flags & PBaseSize )
1343 // PBaseSize is a fallback for PMinSize according to ICCCM 4.1.2.3
1344 // The other way around PMinSize is not a complete fallback for PBaseSize,
1345 // so that's not handled here.
1346 if( ! ( xSizeHint.flags & PMinSize ))
1348 xSizeHint.min_width = xSizeHint.base_width;
1349 xSizeHint.min_height = xSizeHint.base_height;
1352 else
1353 xSizeHint.base_width = xSizeHint.base_height = 0;
1354 if( ! ( xSizeHint.flags & PMaxSize ))
1355 xSizeHint.max_width = xSizeHint.max_height = INT_MAX;
1356 else
1358 xSizeHint.max_width = qMax( xSizeHint.max_width, 1 );
1359 xSizeHint.max_height = qMax( xSizeHint.max_height, 1 );
1361 if( xSizeHint.flags & PResizeInc )
1363 xSizeHint.width_inc = qMax( xSizeHint.width_inc, 1 );
1364 xSizeHint.height_inc = qMax( xSizeHint.height_inc, 1 );
1366 else
1368 xSizeHint.width_inc = 1;
1369 xSizeHint.height_inc = 1;
1371 if( xSizeHint.flags & PAspect )
1372 { // no dividing by zero
1373 xSizeHint.min_aspect.y = qMax( xSizeHint.min_aspect.y, 1 );
1374 xSizeHint.max_aspect.y = qMax( xSizeHint.max_aspect.y, 1 );
1376 else
1378 xSizeHint.min_aspect.x = 1;
1379 xSizeHint.min_aspect.y = INT_MAX;
1380 xSizeHint.max_aspect.x = INT_MAX;
1381 xSizeHint.max_aspect.y = 1;
1383 if( ! ( xSizeHint.flags & PWinGravity ))
1384 xSizeHint.win_gravity = NorthWestGravity;
1385 if( isManaged())
1386 { // update to match restrictions
1387 QSize new_size = adjustedSize();
1388 if( new_size != size() && !isFullScreen())
1390 QRect orig_geometry = geometry();
1391 resizeWithChecks( new_size );
1392 if( ( !isSpecialWindow() || isToolbar()) && !isFullScreen())
1394 // try to keep the window in its xinerama screen if possible,
1395 // if that fails at least keep it visible somewhere
1396 QRect area = workspace()->clientArea( MovementArea, this );
1397 if( area.contains( orig_geometry ))
1398 keepInArea( area );
1399 area = workspace()->clientArea( WorkArea, this );
1400 if( area.contains( orig_geometry ))
1401 keepInArea( area );
1405 updateAllowedActions(); // affects isResizeable()
1408 QSize Client::minSize() const
1410 return rules()->checkMinSize( QSize( xSizeHint.min_width, xSizeHint.min_height ));
1413 QSize Client::maxSize() const
1415 return rules()->checkMaxSize( QSize( xSizeHint.max_width, xSizeHint.max_height ));
1419 Auxiliary function to inform the client about the current window
1420 configuration.
1423 void Client::sendSyntheticConfigureNotify()
1425 XConfigureEvent c;
1426 c.type = ConfigureNotify;
1427 c.send_event = True;
1428 c.event = window();
1429 c.window = window();
1430 c.x = x() + clientPos().x();
1431 c.y = y() + clientPos().y();
1432 c.width = clientSize().width();
1433 c.height = clientSize().height();
1434 c.border_width = 0;
1435 c.above = None;
1436 c.override_redirect = 0;
1437 XSendEvent( display(), c.event, true, StructureNotifyMask, (XEvent*)&c );
1440 const QPoint Client::calculateGravitation( bool invert, int gravity ) const
1442 int dx, dy;
1443 dx = dy = 0;
1445 if( gravity == 0 ) // default (nonsense) value for the argument
1446 gravity = xSizeHint.win_gravity;
1448 // dx, dy specify how the client window moves to make space for the frame
1449 switch (gravity)
1451 case NorthWestGravity: // move down right
1452 default:
1453 dx = border_left;
1454 dy = border_top;
1455 break;
1456 case NorthGravity: // move right
1457 dx = 0;
1458 dy = border_top;
1459 break;
1460 case NorthEastGravity: // move down left
1461 dx = -border_right;
1462 dy = border_top;
1463 break;
1464 case WestGravity: // move right
1465 dx = border_left;
1466 dy = 0;
1467 break;
1468 case CenterGravity:
1469 break; // will be handled specially
1470 case StaticGravity: // don't move
1471 dx = 0;
1472 dy = 0;
1473 break;
1474 case EastGravity: // move left
1475 dx = -border_right;
1476 dy = 0;
1477 break;
1478 case SouthWestGravity: // move up right
1479 dx = border_left ;
1480 dy = -border_bottom;
1481 break;
1482 case SouthGravity: // move up
1483 dx = 0;
1484 dy = -border_bottom;
1485 break;
1486 case SouthEastGravity: // move up left
1487 dx = -border_right;
1488 dy = -border_bottom;
1489 break;
1491 if( gravity != CenterGravity )
1492 { // translate from client movement to frame movement
1493 dx -= border_left;
1494 dy -= border_top;
1496 else
1497 { // center of the frame will be at the same position client center without frame would be
1498 dx = - ( border_left + border_right ) / 2;
1499 dy = - ( border_top + border_bottom ) / 2;
1501 if( !invert )
1502 return QPoint( x() + dx, y() + dy );
1503 else
1504 return QPoint( x() - dx, y() - dy );
1507 void Client::configureRequest( int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool )
1509 if( gravity == 0 ) // default (nonsense) value for the argument
1510 gravity = xSizeHint.win_gravity;
1511 if( value_mask & ( CWX | CWY ))
1513 QPoint new_pos = calculateGravitation( true, gravity ); // undo gravitation
1514 if ( value_mask & CWX )
1515 new_pos.setX( rx );
1516 if ( value_mask & CWY )
1517 new_pos.setY( ry );
1519 // clever(?) workaround for applications like xv that want to set
1520 // the location to the current location but miscalculate the
1521 // frame size due to kwin being a double-reparenting window
1522 // manager
1523 if ( new_pos.x() == x() + clientPos().x() && new_pos.y() == y() + clientPos().y()
1524 && gravity == NorthWestGravity && !from_tool )
1526 new_pos.setX( x());
1527 new_pos.setY( y());
1530 int nw = clientSize().width();
1531 int nh = clientSize().height();
1532 if ( value_mask & CWWidth )
1533 nw = rw;
1534 if ( value_mask & CWHeight )
1535 nh = rh;
1536 QSize ns = sizeForClientSize( QSize( nw, nh ) ); // enforces size if needed
1537 new_pos = rules()->checkPosition( new_pos );
1539 // TODO what to do with maximized windows?
1540 if ( maximizeMode() != MaximizeFull
1541 || ns != size())
1543 QRect orig_geometry = geometry();
1544 GeometryUpdatesBlocker blocker( this );
1545 move( new_pos );
1546 plainResize( ns );
1547 setGeometry( QRect( calculateGravitation( false, gravity ), size()));
1548 updateFullScreenHack( QRect( new_pos, QSize( nw, nh )));
1549 QRect area = workspace()->clientArea( WorkArea, this );
1550 if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen()
1551 && area.contains( orig_geometry ))
1552 keepInArea( area );
1554 // this is part of the kicker-xinerama-hack... it should be
1555 // safe to remove when kicker gets proper ExtendedStrut support;
1556 // see Workspace::updateClientArea() and
1557 // Client::adjustedClientArea()
1558 if (hasStrut ())
1559 workspace() -> updateClientArea ();
1563 if ( value_mask & (CWWidth | CWHeight )
1564 && ! ( value_mask & ( CWX | CWY )) ) // pure resize
1566 int nw = clientSize().width();
1567 int nh = clientSize().height();
1568 if ( value_mask & CWWidth )
1569 nw = rw;
1570 if ( value_mask & CWHeight )
1571 nh = rh;
1572 QSize ns = sizeForClientSize( QSize( nw, nh ) );
1574 if( ns != size()) // don't restore if some app sets its own size again
1576 QRect orig_geometry = geometry();
1577 GeometryUpdatesBlocker blocker( this );
1578 int save_gravity = xSizeHint.win_gravity;
1579 xSizeHint.win_gravity = gravity;
1580 resizeWithChecks( ns );
1581 xSizeHint.win_gravity = save_gravity;
1582 updateFullScreenHack( QRect( calculateGravitation( true, xSizeHint.win_gravity ), QSize( nw, nh )));
1583 if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen())
1585 // try to keep the window in its xinerama screen if possible,
1586 // if that fails at least keep it visible somewhere
1587 QRect area = workspace()->clientArea( MovementArea, this );
1588 if( area.contains( orig_geometry ))
1589 keepInArea( area );
1590 area = workspace()->clientArea( WorkArea, this );
1591 if( area.contains( orig_geometry ))
1592 keepInArea( area );
1596 // No need to send synthetic configure notify event here, either it's sent together
1597 // with geometry change, or there's no need to send it.
1598 // Handling of the real ConfigureRequest event forces sending it, as there it's necessary.
1601 void Client::resizeWithChecks( int w, int h, ForceGeometry_t force )
1603 assert( !shade_geometry_change );
1604 if( isShade())
1606 if( h == border_top + border_bottom )
1608 kWarning(1212) << "Shaded geometry passed for size:" ;
1609 kWarning(1212) << kBacktrace() ;
1612 int newx = x();
1613 int newy = y();
1614 QRect area = workspace()->clientArea( WorkArea, this );
1615 // don't allow growing larger than workarea
1616 if( w > area.width())
1617 w = area.width();
1618 if( h > area.height())
1619 h = area.height();
1620 QSize tmp = adjustedSize( QSize( w, h )); // checks size constraints, including min/max size
1621 w = tmp.width();
1622 h = tmp.height();
1623 switch( xSizeHint.win_gravity )
1625 case NorthWestGravity: // top left corner doesn't move
1626 default:
1627 break;
1628 case NorthGravity: // middle of top border doesn't move
1629 newx = ( newx + width() / 2 ) - ( w / 2 );
1630 break;
1631 case NorthEastGravity: // top right corner doesn't move
1632 newx = newx + width() - w;
1633 break;
1634 case WestGravity: // middle of left border doesn't move
1635 newy = ( newy + height() / 2 ) - ( h / 2 );
1636 break;
1637 case CenterGravity: // middle point doesn't move
1638 newx = ( newx + width() / 2 ) - ( w / 2 );
1639 newy = ( newy + height() / 2 ) - ( h / 2 );
1640 break;
1641 case StaticGravity: // top left corner of _client_ window doesn't move
1642 // since decoration doesn't change, equal to NorthWestGravity
1643 break;
1644 case EastGravity: // // middle of right border doesn't move
1645 newx = newx + width() - w;
1646 newy = ( newy + height() / 2 ) - ( h / 2 );
1647 break;
1648 case SouthWestGravity: // bottom left corner doesn't move
1649 newy = newy + height() - h;
1650 break;
1651 case SouthGravity: // middle of bottom border doesn't move
1652 newx = ( newx + width() / 2 ) - ( w / 2 );
1653 newy = newy + height() - h;
1654 break;
1655 case SouthEastGravity: // bottom right corner doesn't move
1656 newx = newx + width() - w;
1657 newy = newy + height() - h;
1658 break;
1660 // if it would be moved outside of workarea, keep it inside,
1661 // see also Client::computeWorkareaDiff()
1662 if( workarea_diff_x != INT_MIN && w <= area.width()) // was inside and can still fit
1664 if( newx < area.left())
1665 newx = area.left();
1666 if( newx + w > area.right() + 1 )
1667 newx = area.right() + 1 - w;
1668 assert( newx >= area.left() && newx + w <= area.right() + 1 ); // width was checked above
1670 if( workarea_diff_y != INT_MIN && h <= area.height()) // was inside and can still fit
1672 if( newy < area.top())
1673 newy = area.top();
1674 if( newy + h > area.bottom() + 1 )
1675 newy = area.bottom() + 1 - h;
1676 assert( newy >= area.top() && newy + h <= area.bottom() + 1 ); // height was checked above
1678 setGeometry( newx, newy, w, h, force );
1681 // _NET_MOVERESIZE_WINDOW
1682 void Client::NETMoveResizeWindow( int flags, int x, int y, int width, int height )
1684 int gravity = flags & 0xff;
1685 int value_mask = 0;
1686 if( flags & ( 1 << 8 ))
1687 value_mask |= CWX;
1688 if( flags & ( 1 << 9 ))
1689 value_mask |= CWY;
1690 if( flags & ( 1 << 10 ))
1691 value_mask |= CWWidth;
1692 if( flags & ( 1 << 11 ))
1693 value_mask |= CWHeight;
1694 configureRequest( value_mask, x, y, width, height, gravity, true );
1698 Returns whether the window is moveable or has a fixed
1699 position.
1701 bool Client::isMovable() const
1703 if( !motif_may_move || isFullScreen())
1704 return false;
1705 if( isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :)
1706 return false;
1707 if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
1708 return false;
1709 if( rules()->checkPosition( invalidPoint ) != invalidPoint ) // forced position
1710 return false;
1711 return true;
1715 Returns whether the window is moveable across Xinerama screens
1717 bool Client::isMovableAcrossScreens() const
1719 if( !motif_may_move )
1720 return false;
1721 if( isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :)
1722 return false;
1723 if( rules()->checkPosition( invalidPoint ) != invalidPoint ) // forced position
1724 return false;
1725 return true;
1729 Returns whether the window is resizable or has a fixed size.
1731 bool Client::isResizable() const
1733 if( !motif_may_resize || isFullScreen())
1734 return false;
1735 if( isSpecialWindow() || isSplash() || isToolbar())
1736 return false;
1737 if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
1738 return false;
1739 if( rules()->checkSize( QSize()).isValid()) // forced size
1740 return false;
1742 QSize min = minSize();
1743 QSize max = maxSize();
1744 return min.width() < max.width() || min.height() < max.height();
1748 Returns whether the window is maximizable or not
1750 bool Client::isMaximizable() const
1752 { // isMovable() and isResizable() may be false for maximized windows
1753 // with moving/resizing maximized windows disabled
1754 TemporaryAssign< MaximizeMode > tmp( max_mode, MaximizeRestore );
1755 if( !isMovable() || !isResizable() || isToolbar()) // SELI isToolbar() ?
1756 return false;
1758 if ( maximizeMode() != MaximizeRestore )
1759 return true;
1760 QSize max = maxSize();
1761 #if 0
1762 if( max.width() < 32767 || max.height() < 32767 ) // sizes are 16bit with X
1763 return false;
1764 #else
1765 // apparently there are enough apps which specify some arbitrary value
1766 // for their maximum size just for the fun of it
1767 QSize areasize = workspace()->clientArea( MaximizeArea, this ).size();
1768 if( max.width() < areasize.width() || max.height() < areasize.height())
1769 return false;
1770 #endif
1771 return true;
1776 Reimplemented to inform the client about the new window position.
1778 void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force )
1780 // this code is also duplicated in Client::plainResize()
1781 // Ok, the shading geometry stuff. Generally, code doesn't care about shaded geometry,
1782 // simply because there are too many places dealing with geometry. Those places
1783 // ignore shaded state and use normal geometry, which they usually should get
1784 // from adjustedSize(). Such geometry comes here, and if the window is shaded,
1785 // the geometry is used only for client_size, since that one is not used when
1786 // shading. Then the frame geometry is adjusted for the shaded geometry.
1787 // This gets more complicated in the case the code does only something like
1788 // setGeometry( geometry()) - geometry() will return the shaded frame geometry.
1789 // Such code is wrong and should be changed to handle the case when the window is shaded,
1790 // for example using Client::clientSize().
1791 if( shade_geometry_change )
1792 ; // nothing
1793 else if( isShade())
1795 if( h == border_top + border_bottom )
1797 kDebug(1212) << "Shaded geometry passed for size:";
1798 kDebug(1212) << kBacktrace();
1800 else
1802 client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
1803 h = border_top + border_bottom;
1806 else
1808 client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
1810 QRect g( x, y, w, h );
1811 if( block_geometry_updates == 0 && g != rules()->checkGeometry( g ))
1813 kDebug(1212) << "forced geometry fail:" << g << ":" << rules()->checkGeometry( g );
1814 kDebug(1212) << kBacktrace();
1816 if( force == NormalGeometrySet && geom == g && pending_geometry_update == PendingGeometryNone )
1817 return;
1818 geom = g;
1819 updateWorkareaDiffs();
1820 if( block_geometry_updates != 0 )
1822 if( pending_geometry_update == PendingGeometryForced )
1823 {} // maximum, nothing needed
1824 else if( force == ForceGeometrySet )
1825 pending_geometry_update = PendingGeometryForced;
1826 else
1827 pending_geometry_update = PendingGeometryNormal;
1828 return;
1830 bool resized = ( geom_before_block.size() != geom.size() || pending_geometry_update == PendingGeometryForced );
1831 if( resized )
1833 resizeDecoration( QSize( w, h ));
1834 XMoveResizeWindow( display(), frameId(), x, y, w, h );
1835 if( !isShade())
1837 QSize cs = clientSize();
1838 XMoveResizeWindow( display(), wrapperId(), clientPos().x(), clientPos().y(),
1839 cs.width(), cs.height());
1840 XMoveResizeWindow( display(), window(), 0, 0, cs.width(), cs.height());
1842 updateShape();
1844 else
1845 XMoveWindow( display(), frameId(), x, y );
1846 // SELI TODO won't this be too expensive?
1847 sendSyntheticConfigureNotify();
1848 updateWindowRules();
1849 checkMaximizeGeometry();
1850 workspace()->checkActiveScreen( this );
1851 workspace()->updateStackingOrder();
1852 workspace()->checkUnredirect();
1853 if( resized )
1855 discardWindowPixmap();
1856 if( scene != NULL )
1857 scene->windowGeometryShapeChanged( this );
1858 if( effects != NULL )
1859 static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), geom_before_block );
1861 addWorkspaceRepaint( geom_before_block );
1862 addWorkspaceRepaint( geom );
1863 geom_before_block = geom;
1866 void Client::plainResize( int w, int h, ForceGeometry_t force )
1868 // this code is also duplicated in Client::setGeometry(), and it's also commented there
1869 if( shade_geometry_change )
1870 ; // nothing
1871 else if( isShade())
1873 if( h == border_top + border_bottom )
1875 kDebug(1212) << "Shaded geometry passed for size:";
1876 kDebug(1212) << kBacktrace();
1878 else
1880 client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
1881 h = border_top + border_bottom;
1884 else
1886 client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
1888 QSize s( w, h );
1889 if( block_geometry_updates == 0 && s != rules()->checkSize( s ))
1891 kDebug(1212) << "forced size fail:" << s << ":" << rules()->checkSize( s );
1892 kDebug(1212) << kBacktrace();
1894 // resuming geometry updates is handled only in setGeometry()
1895 assert( pending_geometry_update == PendingGeometryNone || block_geometry_updates > 0 );
1896 if( force == NormalGeometrySet && geom.size() == s )
1897 return;
1898 geom.setSize( s );
1899 updateWorkareaDiffs();
1900 if( block_geometry_updates != 0 )
1902 if( pending_geometry_update == PendingGeometryForced )
1903 {} // maximum, nothing needed
1904 else if( force == ForceGeometrySet )
1905 pending_geometry_update = PendingGeometryForced;
1906 else
1907 pending_geometry_update = PendingGeometryNormal;
1908 return;
1910 resizeDecoration( s );
1911 XResizeWindow( display(), frameId(), w, h );
1912 // resizeDecoration( s );
1913 if( !isShade())
1915 QSize cs = clientSize();
1916 XMoveResizeWindow( display(), wrapperId(), clientPos().x(), clientPos().y(),
1917 cs.width(), cs.height());
1918 XMoveResizeWindow( display(), window(), 0, 0, cs.width(), cs.height());
1920 updateShape();
1921 sendSyntheticConfigureNotify();
1922 updateWindowRules();
1923 checkMaximizeGeometry();
1924 workspace()->checkActiveScreen( this );
1925 workspace()->updateStackingOrder();
1926 workspace()->checkUnredirect();
1927 discardWindowPixmap();
1928 if( scene != NULL )
1929 scene->windowGeometryShapeChanged( this );
1930 if( effects != NULL )
1931 static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), geom_before_block );
1932 addWorkspaceRepaint( geom_before_block );
1933 addWorkspaceRepaint( geom );
1934 geom_before_block = geom;
1938 Reimplemented to inform the client about the new window position.
1940 void Client::move( int x, int y, ForceGeometry_t force )
1942 // resuming geometry updates is handled only in setGeometry()
1943 assert( pending_geometry_update == PendingGeometryNone || block_geometry_updates > 0 );
1944 QPoint p( x, y );
1945 if( block_geometry_updates == 0 && p != rules()->checkPosition( p ))
1947 kDebug(1212) << "forced position fail:" << p << ":" << rules()->checkPosition( p );
1948 kDebug(1212) << kBacktrace();
1950 if( force == NormalGeometrySet && geom.topLeft() == p )
1951 return;
1952 geom.moveTopLeft( p );
1953 updateWorkareaDiffs();
1954 if( block_geometry_updates != 0 )
1956 if( pending_geometry_update == PendingGeometryForced )
1957 {} // maximum, nothing needed
1958 else if( force == ForceGeometrySet )
1959 pending_geometry_update = PendingGeometryForced;
1960 else
1961 pending_geometry_update = PendingGeometryNormal;
1962 return;
1964 XMoveWindow( display(), frameId(), x, y );
1965 sendSyntheticConfigureNotify();
1966 updateWindowRules();
1967 checkMaximizeGeometry();
1968 workspace()->checkActiveScreen( this );
1969 workspace()->updateStackingOrder();
1970 workspace()->checkUnredirect();
1971 // client itself is not damaged
1972 addWorkspaceRepaint( geom_before_block );
1973 addWorkspaceRepaint( geom ); // trigger repaint of window's new location
1974 geom_before_block = geom;
1977 void Client::blockGeometryUpdates( bool block )
1979 if( block )
1981 if( block_geometry_updates == 0 )
1982 pending_geometry_update = PendingGeometryNone;
1983 ++block_geometry_updates;
1985 else
1987 if( --block_geometry_updates == 0 )
1989 if( pending_geometry_update != PendingGeometryNone )
1991 if( isShade())
1992 setGeometry( QRect( pos(), adjustedSize()), NormalGeometrySet );
1993 else
1994 setGeometry( geometry(), NormalGeometrySet );
1995 pending_geometry_update = PendingGeometryNone;
2001 void Client::maximize( MaximizeMode m )
2003 setMaximize( m & MaximizeVertical, m & MaximizeHorizontal );
2007 Sets the maximization according to \a vertically and \a horizontally
2009 void Client::setMaximize( bool vertically, bool horizontally )
2010 { // changeMaximize() flips the state, so change from set->flip
2011 changeMaximize(
2012 max_mode & MaximizeVertical ? !vertically : vertically,
2013 max_mode & MaximizeHorizontal ? !horizontally : horizontally,
2014 false );
2017 void Client::changeMaximize( bool vertical, bool horizontal, bool adjust )
2019 if( !isMaximizable())
2020 return;
2022 MaximizeMode old_mode = max_mode;
2023 // 'adjust == true' means to update the size only, e.g. after changing workspace size
2024 if( !adjust )
2026 if( vertical )
2027 max_mode = MaximizeMode( max_mode ^ MaximizeVertical );
2028 if( horizontal )
2029 max_mode = MaximizeMode( max_mode ^ MaximizeHorizontal );
2032 max_mode = rules()->checkMaximize( max_mode );
2033 if( !adjust && max_mode == old_mode )
2034 return;
2036 GeometryUpdatesBlocker blocker( this );
2038 // maximing one way and unmaximizing the other way shouldn't happen,
2039 // so restore first and then maximize the other way
2040 if( ( old_mode == MaximizeVertical && max_mode == MaximizeHorizontal )
2041 || ( old_mode == MaximizeHorizontal && max_mode == MaximizeVertical ))
2043 changeMaximize( false, false, false ); // restore
2047 QRect clientArea = workspace()->clientArea( MaximizeArea, this );
2049 // save sizes for restoring, if maximalizing
2050 if( !adjust && !( y() == clientArea.top() && height() == clientArea.height()))
2052 geom_restore.setTop( y());
2053 geom_restore.setHeight( height());
2055 if( !adjust && !( x() == clientArea.left() && width() == clientArea.width()))
2057 geom_restore.setLeft( x());
2058 geom_restore.setWidth( width());
2061 if( !adjust )
2063 if(( vertical && !(old_mode & MaximizeVertical ))
2064 || ( horizontal && !( old_mode & MaximizeHorizontal )))
2065 Notify::raise( Notify::Maximize );
2066 else
2067 Notify::raise( Notify::UnMaximize );
2070 ForceGeometry_t geom_mode = NormalGeometrySet;
2071 if( decoration != NULL ) // decorations may turn off some borders when maximized
2073 if( checkBorderSizes( false )) // only query, don't resize
2074 geom_mode = ForceGeometrySet;
2077 // restore partial maximizations
2078 if ( old_mode==MaximizeFull && max_mode==MaximizeRestore )
2080 if ( maximizeModeRestore()==MaximizeVertical )
2082 max_mode = MaximizeVertical;
2083 maxmode_restore = MaximizeRestore;
2085 if ( maximizeModeRestore()==MaximizeHorizontal )
2087 max_mode = MaximizeHorizontal;
2088 maxmode_restore = MaximizeRestore;
2092 switch (max_mode)
2095 case MaximizeVertical:
2097 if( old_mode & MaximizeHorizontal ) // actually restoring from MaximizeFull
2099 if( geom_restore.width() == 0 || !clientArea.contains( geom_restore.center() ))
2100 { // needs placement
2101 plainResize( adjustedSize( QSize( width() * 2 / 3, clientArea.height()), SizemodeFixedH ), geom_mode );
2102 workspace()->placeSmart( this, clientArea );
2104 else
2106 setGeometry( QRect(QPoint( geom_restore.x(), clientArea.top()),
2107 adjustedSize(QSize( geom_restore.width(), clientArea.height()), SizemodeFixedH )), geom_mode );
2110 else
2112 setGeometry( QRect(QPoint(x(), clientArea.top()),
2113 adjustedSize(QSize(width(), clientArea.height()), SizemodeFixedH )), geom_mode );
2115 info->setState( NET::MaxVert, NET::Max );
2116 break;
2119 case MaximizeHorizontal:
2121 if( old_mode & MaximizeVertical ) // actually restoring from MaximizeFull
2123 if( geom_restore.height() == 0 || !clientArea.contains( geom_restore.center() ) )
2124 { // needs placement
2125 plainResize( adjustedSize( QSize( clientArea.width(), height() * 2 / 3 ), SizemodeFixedW ), geom_mode );
2126 workspace()->placeSmart( this, clientArea );
2128 else
2130 setGeometry( QRect( QPoint(clientArea.left(), geom_restore.y()),
2131 adjustedSize(QSize(clientArea.width(), geom_restore.height()), SizemodeFixedW )), geom_mode );
2134 else
2136 setGeometry( QRect( QPoint(clientArea.left(), y()),
2137 adjustedSize(QSize(clientArea.width(), height()), SizemodeFixedW )), geom_mode );
2139 info->setState( NET::MaxHoriz, NET::Max );
2140 break;
2143 case MaximizeRestore:
2145 QRect restore = geometry();
2146 // when only partially maximized, geom_restore may not have the other dimension remembered
2147 if( old_mode & MaximizeVertical )
2149 restore.setTop( geom_restore.top());
2150 restore.setBottom( geom_restore.bottom());
2152 if( old_mode & MaximizeHorizontal )
2154 restore.setLeft( geom_restore.left());
2155 restore.setRight( geom_restore.right());
2157 if( !restore.isValid())
2159 QSize s = QSize( clientArea.width()*2/3, clientArea.height()*2/3 );
2160 if( geom_restore.width() > 0 )
2161 s.setWidth( geom_restore.width());
2162 if( geom_restore.height() > 0 )
2163 s.setHeight( geom_restore.height());
2164 plainResize( adjustedSize( s ));
2165 workspace()->placeSmart( this, clientArea );
2166 restore = geometry();
2167 if( geom_restore.width() > 0 )
2168 restore.moveLeft( geom_restore.x());
2169 if( geom_restore.height() > 0 )
2170 restore.moveTop( geom_restore.y());
2172 setGeometry( restore, geom_mode );
2173 if( !clientArea.contains( geom_restore.center() )) // Not restoring to the same screen
2174 workspace()->place( this, clientArea );
2175 info->setState( 0, NET::Max );
2176 break;
2179 case MaximizeFull:
2181 if( !adjust )
2183 if( old_mode & MaximizeVertical )
2184 maxmode_restore = MaximizeVertical;
2185 if( old_mode & MaximizeHorizontal )
2186 maxmode_restore = MaximizeHorizontal;
2188 QSize adjSize = adjustedSize(clientArea.size(), SizemodeMax );
2189 QRect r = QRect(clientArea.topLeft(), adjSize);
2190 setGeometry( r, geom_mode );
2191 info->setState( NET::Max, NET::Max );
2192 break;
2194 default:
2195 break;
2198 updateAllowedActions();
2199 if( decoration != NULL )
2200 decoration->maximizeChange();
2201 updateWindowRules();
2204 void Client::resetMaximize()
2206 if( max_mode == MaximizeRestore )
2207 return;
2208 max_mode = MaximizeRestore;
2209 Notify::raise( Notify::UnMaximize );
2210 info->setState( 0, NET::Max );
2211 updateAllowedActions();
2212 if( decoration != NULL )
2213 decoration->borders( border_left, border_right, border_top, border_bottom );
2214 if( isShade())
2215 setGeometry( QRect( pos(), sizeForClientSize( clientSize())), ForceGeometrySet );
2216 else
2217 setGeometry( geometry(), ForceGeometrySet );
2218 if( decoration != NULL )
2219 decoration->maximizeChange();
2222 void Client::checkMaximizeGeometry()
2224 // when adding new bail-out conditions here, checkMaximizeGeometry() needs to be called
2225 // when after the condition is no longer true
2226 if( isShade())
2227 return;
2228 if( isMove() || isResize()) // this is because of the option to disallow moving/resizing of max-ed windows
2229 return;
2230 // Just in case.
2231 static int recursion_protection = 0;
2232 if( recursion_protection > 3 )
2234 kWarning( 1212 ) << "Check maximize overflow - you loose!" ;
2235 kWarning( 1212 ) << kBacktrace() ;
2236 return;
2238 ++recursion_protection;
2239 QRect max_area = workspace()->clientArea( MaximizeArea, this );
2240 if( geometry() == max_area )
2242 if( max_mode != MaximizeFull )
2243 maximize( MaximizeFull );
2245 else if( x() == max_area.left() && width() == max_area.width())
2247 if( max_mode != MaximizeHorizontal )
2248 maximize( MaximizeHorizontal );
2250 else if( y() == max_area.top() && height() == max_area.height())
2252 if( max_mode != MaximizeVertical )
2253 maximize( MaximizeVertical );
2255 else if( max_mode != MaximizeRestore )
2257 resetMaximize(); // not maximize( MaximizeRestore ), that'd change geometry - this is called from setGeometry()
2259 --recursion_protection;
2262 bool Client::isFullScreenable( bool fullscreen_hack ) const
2264 if( !rules()->checkFullScreen( true ))
2265 return false;
2266 if( fullscreen_hack )
2267 return isNormalWindow();
2268 if( rules()->checkStrictGeometry( false ))
2270 // the app wouldn't fit exactly fullscreen geometry due its strict geometry requirements
2271 QRect fsarea = workspace()->clientArea( FullScreenArea, this );
2272 if( sizeForClientSize( fsarea.size(), SizemodeAny, true ) != fsarea.size())
2273 return false;
2275 // don't check size constrains - some apps request fullscreen despite requesting fixed size
2276 return !isSpecialWindow(); // also better disallow only weird types to go fullscreen
2279 bool Client::userCanSetFullScreen() const
2281 if( fullscreen_mode == FullScreenHack )
2282 return false;
2283 if( !isFullScreenable( false ))
2284 return false;
2285 // isMaximizable() returns false if fullscreen
2286 TemporaryAssign< FullScreenMode > tmp( fullscreen_mode, FullScreenNone );
2287 return isNormalWindow() && isMaximizable();
2290 void Client::setFullScreen( bool set, bool user )
2292 if( !isFullScreen() && !set )
2293 return;
2294 if( fullscreen_mode == FullScreenHack )
2295 return;
2296 if( user && !userCanSetFullScreen())
2297 return;
2298 set = rules()->checkFullScreen( set );
2299 setShade( ShadeNone );
2300 bool was_fs = isFullScreen();
2301 if( !was_fs )
2302 geom_fs_restore = geometry();
2303 fullscreen_mode = set ? FullScreenNormal : FullScreenNone;
2304 if( was_fs == isFullScreen())
2305 return;
2306 StackingUpdatesBlocker blocker1( workspace());
2307 GeometryUpdatesBlocker blocker2( this );
2308 workspace()->updateClientLayer( this ); // active fullscreens get different layer
2309 info->setState( isFullScreen() ? NET::FullScreen : 0, NET::FullScreen );
2310 updateDecoration( false, false );
2311 if( isFullScreen())
2312 if( info->fullscreenMonitors().isSet())
2313 setGeometry( fullscreenMonitorsArea( info->fullscreenMonitors()));
2314 else
2315 setGeometry( workspace()->clientArea( FullScreenArea, this ));
2316 else
2318 if( !geom_fs_restore.isNull())
2319 setGeometry( QRect( geom_fs_restore.topLeft(), adjustedSize( geom_fs_restore.size())));
2320 // TODO isShaded() ?
2321 else
2322 { // does this ever happen?
2323 setGeometry( workspace()->clientArea( MaximizeArea, this ));
2326 updateWindowRules();
2327 workspace()->checkUnredirect();
2331 void Client::updateFullscreenMonitors( NETFullscreenMonitors topology )
2333 int nscreens = Kephal::ScreenUtils::numScreens();
2335 // kDebug( 1212 ) << "incoming request with top: " << topology.top << " bottom: " << topology.bottom
2336 // << " left: " << topology.left << " right: " << topology.right
2337 // << ", we have: " << nscreens << " screens.";
2339 if( topology.top >= nscreens ||
2340 topology.bottom >= nscreens ||
2341 topology.left >= nscreens ||
2342 topology.right >= nscreens )
2344 kWarning( 1212 ) << "fullscreenMonitors update failed. request higher than number of screens.";
2345 return;
2348 info->setFullscreenMonitors( topology );
2349 if( isFullScreen())
2350 setGeometry( fullscreenMonitorsArea( topology ));
2355 Calculates the bounding rectangle defined by the 4 monitor indices indicating the
2356 top, bottom, left, and right edges of the window when the fullscreen state is enabled.
2358 QRect Client::fullscreenMonitorsArea(NETFullscreenMonitors requestedTopology) const
2360 QRect top, bottom, left, right, total;
2362 top = Kephal::ScreenUtils::screenGeometry( requestedTopology.top );
2363 bottom = Kephal::ScreenUtils::screenGeometry(requestedTopology.bottom );
2364 left = Kephal::ScreenUtils::screenGeometry(requestedTopology.left );
2365 right = Kephal::ScreenUtils::screenGeometry(requestedTopology.right );
2366 total = top.united( bottom.united( left.united( right ) ) );
2368 // kDebug( 1212 ) << "top: " << top << " bottom: " << bottom
2369 // << " left: " << left << " right: " << right;
2370 // kDebug( 1212 ) << "returning rect: " << total;
2371 return total;
2375 int Client::checkFullScreenHack( const QRect& geom ) const
2377 // if it's noborder window, and has size of one screen or the whole desktop geometry, it's fullscreen hack
2378 if( noBorder() && app_noborder && isFullScreenable( true ))
2380 if( geom.size() == workspace()->clientArea( FullArea, geom.center(), desktop()).size())
2381 return 2; // full area fullscreen hack
2382 if( geom.size() == workspace()->clientArea( ScreenArea, geom.center(), desktop()).size())
2383 return 1; // xinerama-aware fullscreen hack
2385 return 0;
2388 void Client::updateFullScreenHack( const QRect& geom )
2390 int type = checkFullScreenHack( geom );
2391 if( fullscreen_mode == FullScreenNone && type != 0 )
2393 fullscreen_mode = FullScreenHack;
2394 updateDecoration( false, false );
2395 QRect geom;
2396 if( rules()->checkStrictGeometry( false ))
2398 geom = type == 2 // 1 - it's xinerama-aware fullscreen hack, 2 - it's full area
2399 ? workspace()->clientArea( FullArea, geom.center(), desktop())
2400 : workspace()->clientArea( ScreenArea, geom.center(), desktop());
2402 else
2403 geom = workspace()->clientArea( FullScreenArea, geom.center(), desktop());
2404 setGeometry( geom );
2406 else if( fullscreen_mode == FullScreenHack && type == 0 )
2408 fullscreen_mode = FullScreenNone;
2409 updateDecoration( false, false );
2410 // whoever called this must setup correct geometry
2412 StackingUpdatesBlocker blocker( workspace());
2413 workspace()->updateClientLayer( this ); // active fullscreens get different layer
2416 static QRect* visible_bound = 0;
2417 static GeometryTip* geometryTip = 0;
2419 void Client::drawbound( const QRect& geom )
2421 assert( visible_bound == NULL );
2422 visible_bound = new QRect( geom );
2423 doDrawbound( *visible_bound, false );
2426 void Client::clearbound()
2428 if( visible_bound == NULL )
2429 return;
2430 doDrawbound( *visible_bound, true );
2431 delete visible_bound;
2432 visible_bound = 0;
2435 void Client::doDrawbound( const QRect& geom, bool clear )
2437 if( decoration != NULL && decoration->drawbound( geom, clear ))
2438 return; // done by decoration
2439 XGCValues xgc;
2440 xgc.function = GXxor;
2441 xgc.foreground = WhitePixel( display(), DefaultScreen( display()));
2442 xgc.line_width = 5;
2443 xgc.subwindow_mode = IncludeInferiors;
2444 GC gc = XCreateGC( display(), DefaultRootWindow( display()),
2445 GCFunction | GCForeground | GCLineWidth | GCSubwindowMode, &xgc );
2446 // the line is 5 pixel thick, so compensate for the extra two pixels
2447 // on outside (#88657)
2448 QRect g = geom;
2449 if( g.width() > 5 )
2451 g.setLeft( g.left() + 2 );
2452 g.setRight( g.right() - 2 );
2454 if( g.height() > 5 )
2456 g.setTop( g.top() + 2 );
2457 g.setBottom( g.bottom() - 2 );
2459 XDrawRectangle( display(), DefaultRootWindow( display()), gc, g.x(), g.y(), g.width(), g.height());
2460 XFreeGC( display(), gc );
2463 void Client::positionGeometryTip()
2465 assert( isMove() || isResize());
2466 // Position and Size display
2467 if (options->showGeometryTip())
2469 if( !geometryTip )
2470 { // save under is not necessary with opaque, and seem to make things slower
2471 bool save_under = ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
2472 || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque );
2473 geometryTip = new GeometryTip( &xSizeHint, save_under );
2475 QRect wgeom( moveResizeGeom ); // position of the frame, size of the window itself
2476 wgeom.setWidth( wgeom.width() - ( width() - clientSize().width()));
2477 wgeom.setHeight( wgeom.height() - ( height() - clientSize().height()));
2478 if( isShade())
2479 wgeom.setHeight( 0 );
2480 geometryTip->setGeometry( wgeom );
2481 if( !geometryTip->isVisible())
2482 geometryTip->show();
2483 geometryTip->raise();
2487 class EatAllPaintEvents
2488 : public QObject
2490 protected:
2491 virtual bool eventFilter( QObject* o, QEvent* e )
2492 { return e->type() == QEvent::Paint && o != geometryTip; }
2495 static EatAllPaintEvents* eater = 0;
2497 bool Client::startMoveResize()
2499 assert( !moveResizeMode );
2500 assert( QWidget::keyboardGrabber() == NULL );
2501 assert( QWidget::mouseGrabber() == NULL );
2502 stopDelayedMoveResize();
2503 if( QApplication::activePopupWidget() != NULL )
2504 return false; // popups have grab
2505 bool has_grab = false;
2506 // This reportedly improves smoothness of the moveresize operation,
2507 // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug*
2508 // (http://lists.kde.org/?t=107302193400001&r=1&w=2)
2509 XSetWindowAttributes attrs;
2510 QRect r = workspace()->clientArea( FullArea, this );
2511 move_resize_grab_window = XCreateWindow( display(), rootWindow(), r.x(), r.y(),
2512 r.width(), r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, 0, &attrs );
2513 XMapRaised( display(), move_resize_grab_window );
2514 if( XGrabPointer( display(), move_resize_grab_window, False,
2515 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask,
2516 GrabModeAsync, GrabModeAsync, move_resize_grab_window, cursor.handle(), xTime() ) == Success )
2517 has_grab = true;
2518 if( grabXKeyboard( frameId()))
2519 has_grab = move_resize_has_keyboard_grab = true;
2520 if( !has_grab ) // at least one grab is necessary in order to be able to finish move/resize
2522 XDestroyWindow( display(), move_resize_grab_window );
2523 move_resize_grab_window = None;
2524 return false;
2526 if ( maximizeMode() != MaximizeRestore )
2527 resetMaximize();
2528 moveResizeMode = true;
2529 workspace()->setClientIsMoving(this);
2530 initialMoveResizeGeom = moveResizeGeom = geometry();
2531 checkUnrestrictedMoveResize();
2532 if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
2533 || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
2535 grabXServer();
2536 kapp->sendPostedEvents();
2537 // we have server grab -> nothing should cause paint events
2538 // unfortunately, that's not completely true, Qt may generate
2539 // paint events on some widgets due to FocusIn(?)
2540 // eat them, otherwise XOR painting will be broken (#58054)
2541 // paint events for the geometrytip need to be allowed, though
2542 eater = new EatAllPaintEvents;
2543 // not needed anymore? kapp->installEventFilter( eater );
2545 Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart );
2546 if( effects )
2547 static_cast<EffectsHandlerImpl*>(effects)->windowUserMovedResized( effectWindow(), true, false );
2548 if( options->electricBorders() == Options::ElectricMoveOnly )
2549 workspace()->reserveElectricBorderSwitching( true );
2550 return true;
2553 void Client::finishMoveResize( bool cancel )
2555 leaveMoveResize();
2556 if( cancel )
2557 setGeometry( initialMoveResizeGeom );
2558 else
2559 setGeometry( moveResizeGeom );
2560 checkMaximizeGeometry();
2561 // FRAME update();
2562 Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd );
2563 if( effects )
2564 static_cast<EffectsHandlerImpl*>(effects)->windowUserMovedResized( effectWindow(), false, true );
2567 void Client::leaveMoveResize()
2569 clearbound();
2570 if (geometryTip)
2572 geometryTip->hide();
2573 delete geometryTip;
2574 geometryTip = NULL;
2576 if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
2577 || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
2578 ungrabXServer();
2579 if( move_resize_has_keyboard_grab )
2580 ungrabXKeyboard();
2581 move_resize_has_keyboard_grab = false;
2582 XUngrabPointer( display(), xTime() );
2583 XDestroyWindow( display(), move_resize_grab_window );
2584 move_resize_grab_window = None;
2585 workspace()->setClientIsMoving(0);
2586 if( move_faked_activity )
2587 workspace()->unfakeActivity( this );
2588 move_faked_activity = false;
2589 moveResizeMode = false;
2590 delete eater;
2591 eater = 0;
2592 delete sync_timeout;
2593 sync_timeout = NULL;
2594 if( options->electricBorders() == Options::ElectricMoveOnly )
2595 workspace()->reserveElectricBorderSwitching( false );
2598 // This function checks if it actually makes sense to perform a restricted move/resize.
2599 // If e.g. the titlebar is already outside of the workarea, there's no point in performing
2600 // a restricted move resize, because then e.g. resize would also move the window (#74555).
2601 // NOTE: Most of it is duplicated from handleMoveResize().
2602 void Client::checkUnrestrictedMoveResize()
2604 if( unrestrictedMoveResize )
2605 return;
2606 QRect desktopArea = workspace()->clientArea( WorkArea, moveResizeGeom.center(), desktop());
2607 int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
2608 // restricted move/resize - keep at least part of the titlebar always visible
2609 // how much must remain visible when moved away in that direction
2610 left_marge = qMin( 100 + border_right, moveResizeGeom.width());
2611 right_marge = qMin( 100 + border_left, moveResizeGeom.width());
2612 // width/height change with opaque resizing, use the initial ones
2613 titlebar_marge = initialMoveResizeGeom.height();
2614 top_marge = border_bottom;
2615 bottom_marge = border_top;
2616 if( isResize())
2618 if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
2619 unrestrictedMoveResize = true;
2620 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2621 unrestrictedMoveResize = true;
2622 if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2623 unrestrictedMoveResize = true;
2624 if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2625 unrestrictedMoveResize = true;
2626 if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
2627 unrestrictedMoveResize = true;
2629 if( isMove())
2631 if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
2632 unrestrictedMoveResize = true;
2633 // no need to check top_marge, titlebar_marge already handles it
2634 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2635 unrestrictedMoveResize = true;
2636 if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2637 unrestrictedMoveResize = true;
2638 if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2639 unrestrictedMoveResize = true;
2643 // When the user pressed mouse on the titlebar, don't activate move immediatelly,
2644 // since it may be just a click. Activate instead after a delay. Move used to be
2645 // activated only after moving by several pixels, but that looks bad.
2646 void Client::startDelayedMoveResize()
2648 delete delayedMoveResizeTimer;
2649 delayedMoveResizeTimer = new QTimer( this );
2650 connect( delayedMoveResizeTimer, SIGNAL( timeout()), this, SLOT( delayedMoveResize()));
2651 delayedMoveResizeTimer->setSingleShot( true );
2652 delayedMoveResizeTimer->start( QApplication::doubleClickInterval());
2655 void Client::stopDelayedMoveResize()
2657 delete delayedMoveResizeTimer;
2658 delayedMoveResizeTimer = NULL;
2661 void Client::delayedMoveResize()
2663 assert( buttonDown );
2664 if( !startMoveResize())
2665 buttonDown = false;
2666 updateCursor();
2667 stopDelayedMoveResize();
2670 void Client::handleMoveResize( int x, int y, int x_root, int y_root )
2672 if(( mode == PositionCenter && !isMovableAcrossScreens() )
2673 || ( mode != PositionCenter && ( isShade() || !isResizable())))
2674 return;
2676 if ( !moveResizeMode )
2678 QPoint p( QPoint( x, y ) - moveOffset );
2679 if (p.manhattanLength() >= 6)
2681 if( !startMoveResize())
2683 buttonDown = false;
2684 updateCursor();
2685 return;
2687 updateCursor();
2689 else
2690 return;
2693 // ShadeHover or ShadeActive, ShadeNormal was already avoided above
2694 if ( mode != PositionCenter && shade_mode != ShadeNone )
2695 setShade( ShadeNone );
2697 QPoint globalPos( x_root, y_root );
2698 // these two points limit the geometry rectangle, i.e. if bottomleft resizing is done,
2699 // the bottomleft corner should be at is at (topleft.x(), bottomright().y())
2700 QPoint topleft = globalPos - moveOffset;
2701 QPoint bottomright = globalPos + invertedMoveOffset;
2702 QRect previousMoveResizeGeom = moveResizeGeom;
2704 // TODO move whole group when moving its leader or when the leader is not mapped?
2706 // compute bounds
2707 // NOTE: This is duped in checkUnrestrictedMoveResize().
2708 QRect desktopArea = workspace()->clientArea( WorkArea, globalPos, desktop());
2709 int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
2710 if( unrestrictedMoveResize ) // unrestricted, just don't let it go out completely
2711 left_marge = right_marge = top_marge = bottom_marge = titlebar_marge = 5;
2712 else // restricted move/resize - keep at least part of the titlebar always visible
2714 // how much must remain visible when moved away in that direction
2715 left_marge = qMin( 100 + border_right, moveResizeGeom.width());
2716 right_marge = qMin( 100 + border_left, moveResizeGeom.width());
2717 // width/height change with opaque resizing, use the initial ones
2718 titlebar_marge = initialMoveResizeGeom.height();
2719 top_marge = border_bottom;
2720 bottom_marge = border_top;
2723 bool update = false;
2724 if( isResize())
2726 // first resize (without checking constrains), then snap, then check bounds, then check constrains
2727 QRect orig = initialMoveResizeGeom;
2728 Sizemode sizemode = SizemodeAny;
2729 switch ( mode )
2731 case PositionTopLeft:
2732 moveResizeGeom = QRect( topleft, orig.bottomRight() ) ;
2733 break;
2734 case PositionBottomRight:
2735 moveResizeGeom = QRect( orig.topLeft(), bottomright ) ;
2736 break;
2737 case PositionBottomLeft:
2738 moveResizeGeom = QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ;
2739 break;
2740 case PositionTopRight:
2741 moveResizeGeom = QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
2742 break;
2743 case PositionTop:
2744 moveResizeGeom = QRect( QPoint( orig.left(), topleft.y() ), orig.bottomRight() ) ;
2745 sizemode = SizemodeFixedH; // try not to affect height
2746 break;
2747 case PositionBottom:
2748 moveResizeGeom = QRect( orig.topLeft(), QPoint( orig.right(), bottomright.y() ) ) ;
2749 sizemode = SizemodeFixedH;
2750 break;
2751 case PositionLeft:
2752 moveResizeGeom = QRect( QPoint( topleft.x(), orig.top() ), orig.bottomRight() ) ;
2753 sizemode = SizemodeFixedW;
2754 break;
2755 case PositionRight:
2756 moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), orig.bottom() ) ) ;
2757 sizemode = SizemodeFixedW;
2758 break;
2759 case PositionCenter:
2760 default:
2761 abort();
2762 break;
2765 // adjust new size to snap to other windows/borders
2766 moveResizeGeom = workspace()->adjustClientSize( this, moveResizeGeom, mode );
2768 // NOTE: This is duped in checkUnrestrictedMoveResize().
2769 if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
2770 moveResizeGeom.setBottom( desktopArea.top() + top_marge );
2771 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2772 moveResizeGeom.setTop( desktopArea.bottom() - bottom_marge );
2773 if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2774 moveResizeGeom.setRight( desktopArea.left() + left_marge );
2775 if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2776 moveResizeGeom.setLeft(desktopArea.right() - right_marge );
2777 if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
2778 moveResizeGeom.setTop( desktopArea.top());
2780 QSize size = adjustedSize( moveResizeGeom.size(), sizemode );
2781 // the new topleft and bottomright corners (after checking size constrains), if they'll be needed
2782 topleft = QPoint( moveResizeGeom.right() - size.width() + 1, moveResizeGeom.bottom() - size.height() + 1 );
2783 bottomright = QPoint( moveResizeGeom.left() + size.width() - 1, moveResizeGeom.top() + size.height() - 1 );
2784 orig = moveResizeGeom;
2785 switch ( mode )
2786 { // these 4 corners ones are copied from above
2787 case PositionTopLeft:
2788 moveResizeGeom = QRect( topleft, orig.bottomRight() ) ;
2789 break;
2790 case PositionBottomRight:
2791 moveResizeGeom = QRect( orig.topLeft(), bottomright ) ;
2792 break;
2793 case PositionBottomLeft:
2794 moveResizeGeom = QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ;
2795 break;
2796 case PositionTopRight:
2797 moveResizeGeom = QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
2798 break;
2799 // The side ones can't be copied exactly - if aspect ratios are specified, both dimensions may change.
2800 // Therefore grow to the right/bottom if needed.
2801 // TODO it should probably obey gravity rather than always using right/bottom ?
2802 case PositionTop:
2803 moveResizeGeom = QRect( QPoint( orig.left(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
2804 break;
2805 case PositionBottom:
2806 moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ;
2807 break;
2808 case PositionLeft:
2809 moveResizeGeom = QRect( QPoint( topleft.x(), orig.top() ), QPoint( orig.right(), bottomright.y()));
2810 break;
2811 case PositionRight:
2812 moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ;
2813 break;
2814 case PositionCenter:
2815 default:
2816 abort();
2817 break;
2819 if( moveResizeGeom.size() != previousMoveResizeGeom.size())
2820 update = true;
2822 else if( isMove())
2824 assert( mode == PositionCenter );
2825 if( !isMovable() ) // isMovableAcrossScreens() must have been true to get here
2826 { // Special moving of maximized windows on Xinerama screens
2827 int screen = workspace()->screenNumber( globalPos );
2828 moveResizeGeom = workspace()->clientArea( MaximizeArea, screen, 0 );
2830 else
2832 // first move, then snap, then check bounds
2833 moveResizeGeom.moveTopLeft( topleft );
2834 moveResizeGeom.moveTopLeft( workspace()->adjustClientPosition( this, moveResizeGeom.topLeft(),
2835 unrestrictedMoveResize ));
2836 // NOTE: This is duped in checkUnrestrictedMoveResize().
2837 if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
2838 moveResizeGeom.moveBottom( desktopArea.top() + titlebar_marge - 1 );
2839 // no need to check top_marge, titlebar_marge already handles it
2840 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2841 moveResizeGeom.moveTop( desktopArea.bottom() - bottom_marge );
2842 if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2843 moveResizeGeom.moveRight( desktopArea.left() + left_marge );
2844 if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2845 moveResizeGeom.moveLeft(desktopArea.right() - right_marge );
2847 if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft())
2848 update = true;
2850 else
2851 abort();
2853 if( isResize())
2855 if( sync_timeout != NULL )
2857 sync_resize_pending = true;
2858 return;
2862 if( update )
2863 performMoveResize();
2864 if ( isMove() )
2865 workspace()->checkElectricBorder(globalPos, xTime());
2868 void Client::performMoveResize()
2870 #ifdef HAVE_XSYNC
2871 if( isResize() && sync_counter != None )
2873 sync_timeout = new QTimer( this );
2874 connect( sync_timeout, SIGNAL( timeout()), SLOT( syncTimeout()));
2875 sync_timeout->setSingleShot( true );
2876 sync_timeout->start( 500 );
2877 sendSyncRequest();
2879 #endif
2880 sync_resize_pending = false;
2881 if( rules()->checkMoveResizeMode
2882 ( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque )
2884 setGeometry( moveResizeGeom );
2885 positionGeometryTip();
2887 else if( rules()->checkMoveResizeMode
2888 ( isResize() ? options->resizeMode : options->moveMode ) == Options::Transparent )
2890 clearbound(); // it's necessary to move the geometry tip when there's no outline
2891 positionGeometryTip(); // shown, otherwise it would cause repaint problems in case
2892 drawbound( moveResizeGeom ); // they overlap; the paint event will come after this,
2893 } // so the geometry tip will be painted above the outline
2894 if( effects )
2895 static_cast<EffectsHandlerImpl*>(effects)->windowUserMovedResized( effectWindow(), false, false );
2898 void Client::syncTimeout()
2900 sync_timeout->deleteLater();
2901 sync_timeout = NULL;
2902 if( sync_resize_pending )
2903 performMoveResize();
2906 } // namespace