Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / kwin / geometry.cpp
blobf072a46241561de9eb69e82cea48e3dba33faa20
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>
43 #include <QDesktopWidget>
45 namespace KWin
48 //********************************************
49 // Workspace
50 //********************************************
52 /*!
53 Resizes the workspace after an XRANDR screen size change
55 void Workspace::desktopResized()
57 QRect geom = QApplication::desktop()->geometry();
58 NETSize desktop_geometry;
59 desktop_geometry.width = geom.width();
60 desktop_geometry.height = geom.height();
61 rootInfo->setDesktopGeometry( -1, desktop_geometry );
63 updateClientArea();
64 destroyElectricBorders();
65 updateElectricBorders();
66 if( compositing() )
68 finishCompositing();
69 QTimer::singleShot( 0, this, SLOT( setupCompositing() ) );
73 /*!
74 Updates the current client areas according to the current clients.
76 If the area changes or force is true, the new areas are propagated to the world.
78 The client area is the area that is available for clients (that
79 which is not taken by windows like panels, the top-of-screen menu
80 etc).
82 \sa clientArea()
85 void Workspace::updateClientArea( bool force )
87 QDesktopWidget *desktopwidget = KApplication::desktop();
88 int nscreens = desktopwidget -> numScreens ();
89 // kDebug () << "screens: " << nscreens;
90 QRect* new_wareas = new QRect[ numberOfDesktops() + 1 ];
91 QRect** new_sareas = new QRect*[ numberOfDesktops() + 1];
92 QRect* screens = new QRect [ nscreens ];
93 QRect desktopArea = desktopwidget -> geometry ();
94 for( int iS = 0;
95 iS < nscreens;
96 iS ++ )
98 screens [iS] = desktopwidget -> screenGeometry (iS);
100 for( int i = 1;
101 i <= numberOfDesktops();
102 ++i )
104 new_wareas[ i ] = desktopArea;
105 new_sareas[ i ] = new QRect [ nscreens ];
106 for( int iS = 0;
107 iS < nscreens;
108 iS ++ )
109 new_sareas[ i ][ iS ] = screens[ iS ];
111 for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it)
113 if( !(*it)->hasStrut())
114 continue;
115 QRect r = (*it)->adjustedClientArea( desktopArea, desktopArea );
116 if( (*it)->isOnAllDesktops())
117 for( int i = 1;
118 i <= numberOfDesktops();
119 ++i )
121 new_wareas[ i ] = new_wareas[ i ].intersected( r );
122 for( int iS = 0;
123 iS < nscreens;
124 iS ++ )
125 new_sareas[ i ][ iS ] =
126 new_sareas[ i ][ iS ].intersected(
127 (*it)->adjustedClientArea( desktopArea, screens[ iS ] )
130 else
132 new_wareas[ (*it)->desktop() ] = new_wareas[ (*it)->desktop() ].intersected( r );
133 for( int iS = 0;
134 iS < nscreens;
135 iS ++ )
137 // kDebug () << "adjusting new_sarea: " << screens[ iS ];
138 new_sareas[ (*it)->desktop() ][ iS ] =
139 new_sareas[ (*it)->desktop() ][ iS ].intersected(
140 (*it)->adjustedClientArea( desktopArea, screens[ iS ] )
145 #if 0
146 for( int i = 1;
147 i <= numberOfDesktops();
148 ++i )
150 for( int iS = 0;
151 iS < nscreens;
152 iS ++ )
153 kDebug () << "new_sarea: " << new_sareas[ i ][ iS ];
155 #endif
156 // TODO topmenu update for screenarea changes?
157 if( topmenu_space != NULL )
159 QRect topmenu_area = desktopArea;
160 topmenu_area.setTop( topMenuHeight());
161 for( int i = 1;
162 i <= numberOfDesktops();
163 ++i )
164 new_wareas[ i ] = new_wareas[ i ].intersected( topmenu_area );
167 bool changed = force;
169 if (! screenarea)
170 changed = true;
172 for( int i = 1;
173 !changed && i <= numberOfDesktops();
174 ++i )
176 if( workarea[ i ] != new_wareas[ i ] )
177 changed = true;
178 for( int iS = 0;
179 iS < nscreens;
180 iS ++ )
181 if (new_sareas[ i ][ iS ] != screenarea [ i ][ iS ])
182 changed = true;
185 if ( changed )
187 delete[] workarea;
188 workarea = new_wareas;
189 new_wareas = NULL;
190 delete[] screenarea;
191 screenarea = new_sareas;
192 new_sareas = NULL;
193 NETRect r;
194 for( int i = 1; i <= numberOfDesktops(); i++)
196 r.pos.x = workarea[ i ].x();
197 r.pos.y = workarea[ i ].y();
198 r.size.width = workarea[ i ].width();
199 r.size.height = workarea[ i ].height();
200 rootInfo->setWorkArea( i, r );
203 updateTopMenuGeometry();
204 for( ClientList::ConstIterator it = clients.begin();
205 it != clients.end();
206 ++it)
207 (*it)->checkWorkspacePosition();
208 for( ClientList::ConstIterator it = desktops.begin();
209 it != desktops.end();
210 ++it)
211 (*it)->checkWorkspacePosition();
213 delete[] screens;
214 delete[] new_sareas;
215 delete[] new_wareas;
218 void Workspace::updateClientArea()
220 updateClientArea( false );
225 returns the area available for clients. This is the desktop
226 geometry minus windows on the dock. Placement algorithms should
227 refer to this rather than geometry().
229 \sa geometry()
231 QRect Workspace::clientArea( clientAreaOption opt, int screen, int desktop ) const
233 if( desktop == NETWinInfo::OnAllDesktops || desktop == 0 )
234 desktop = currentDesktop();
235 QDesktopWidget *desktopwidget = KApplication::desktop();
236 QRect sarea = screenarea // may be NULL during KWin initialization
237 ? screenarea[ desktop ][ screen ]
238 : desktopwidget->screenGeometry( screen );
239 QRect warea = workarea[ desktop ].isNull()
240 ? QApplication::desktop()->geometry()
241 : workarea[ desktop ];
242 switch (opt)
244 case MaximizeArea:
245 if (options->xineramaMaximizeEnabled)
246 return sarea;
247 else
248 return warea;
249 case MaximizeFullArea:
250 if (options->xineramaMaximizeEnabled)
251 return desktopwidget->screenGeometry( screen );
252 else
253 return desktopwidget->geometry();
254 case FullScreenArea:
255 if (options->xineramaFullscreenEnabled)
256 return desktopwidget->screenGeometry( screen );
257 else
258 return desktopwidget->geometry();
259 case PlacementArea:
260 if (options->xineramaPlacementEnabled)
261 return sarea;
262 else
263 return warea;
264 case MovementArea:
265 if (options->xineramaMovementEnabled)
266 return desktopwidget->screenGeometry( screen );
267 else
268 return desktopwidget->geometry();
269 case WorkArea:
270 return warea;
271 case FullArea:
272 return desktopwidget->geometry();
273 case ScreenArea:
274 return desktopwidget->screenGeometry( screen );
276 assert( false );
277 return QRect();
280 QRect Workspace::clientArea( clientAreaOption opt, const QPoint& p, int desktop ) const
282 QDesktopWidget *desktopwidget = KApplication::desktop();
283 int screen = desktopwidget->isVirtualDesktop() ? desktopwidget->screenNumber( p ) : desktopwidget->primaryScreen();
284 if( screen < 0 )
285 screen = desktopwidget->primaryScreen();
286 return clientArea( opt, screen, desktop );
289 QRect Workspace::clientArea( clientAreaOption opt, const Client* c ) const
291 return clientArea( opt, c->geometry().center(), c->desktop());
296 Client \a c is moved around to position \a pos. This gives the
297 workspace the opportunity to interveniate and to implement
298 snap-to-windows functionality.
300 QPoint Workspace::adjustClientPosition( Client* c, QPoint pos )
302 //CT 16mar98, 27May98 - magics: BorderSnapZone, WindowSnapZone
303 //CT adapted for kwin on 25Nov1999
304 //aleXXX 02Nov2000 added second snapping mode
305 if (options->windowSnapZone || options->borderSnapZone )
307 const bool sOWO=options->snapOnlyWhenOverlapping;
308 const QRect maxRect = clientArea(MovementArea, pos+c->rect().center(), c->desktop());
309 const int xmin = maxRect.left();
310 const int xmax = maxRect.right()+1; //desk size
311 const int ymin = maxRect.top();
312 const int ymax = maxRect.bottom()+1;
314 const int cx(pos.x());
315 const int cy(pos.y());
316 const int cw(c->width());
317 const int ch(c->height());
318 const int rx(cx+cw);
319 const int ry(cy+ch); //these don't change
321 int nx(cx), ny(cy); //buffers
322 int deltaX(xmax);
323 int deltaY(ymax); //minimum distance to other clients
325 int lx, ly, lrx, lry; //coords and size for the comparison client, l
327 // border snap
328 int snap = options->borderSnapZone; //snap trigger
329 if (snap)
331 if ((sOWO?(cx<xmin):true) && (qAbs(xmin-cx)<snap))
333 deltaX = xmin-cx;
334 nx = xmin;
336 if ((sOWO?(rx>xmax):true) && (qAbs(rx-xmax)<snap) && (qAbs(xmax-rx) < deltaX))
338 deltaX = rx-xmax;
339 nx = xmax - cw;
342 if ((sOWO?(cy<ymin):true) && (qAbs(ymin-cy)<snap))
344 deltaY = ymin-cy;
345 ny = ymin;
347 if ((sOWO?(ry>ymax):true) && (qAbs(ry-ymax)<snap) && (qAbs(ymax-ry) < deltaY))
349 deltaY =ry-ymax;
350 ny = ymax - ch;
354 // windows snap
355 snap = options->windowSnapZone;
356 if (snap)
358 QList<Client *>::ConstIterator l;
359 for (l = clients.begin();l != clients.end();++l )
361 if ((*l)->isOnDesktop(currentDesktop()) &&
362 !(*l)->isMinimized()
363 && (*l) != c )
365 lx = (*l)->x();
366 ly = (*l)->y();
367 lrx = lx + (*l)->width();
368 lry = ly + (*l)->height();
370 if ( (( cy <= lry ) && ( cy >= ly )) ||
371 (( ry >= ly ) && ( ry <= lry )) ||
372 (( cy <= ly ) && ( ry >= lry )) )
374 if ((sOWO?(cx<lrx):true) && (qAbs(lrx-cx)<snap) && ( qAbs(lrx -cx) < deltaX) )
376 deltaX = qAbs( lrx - cx );
377 nx = lrx;
379 if ((sOWO?(rx>lx):true) && (qAbs(rx-lx)<snap) && ( qAbs( rx - lx )<deltaX) )
381 deltaX = qAbs(rx - lx);
382 nx = lx - cw;
386 if ( (( cx <= lrx ) && ( cx >= lx )) ||
387 (( rx >= lx ) && ( rx <= lrx )) ||
388 (( cx <= lx ) && ( rx >= lrx )) )
390 if ((sOWO?(cy<lry):true) && (qAbs(lry-cy)<snap) && (qAbs( lry -cy ) < deltaY))
392 deltaY = qAbs( lry - cy );
393 ny = lry;
395 //if ( (qAbs( ry-ly ) < snap) && (qAbs( ry - ly ) < deltaY ))
396 if ((sOWO?(ry>ly):true) && (qAbs(ry-ly)<snap) && (qAbs( ry - ly ) < deltaY ))
398 deltaY = qAbs( ry - ly );
399 ny = ly - ch;
405 pos = QPoint(nx, ny);
407 return pos;
410 QRect Workspace::adjustClientSize( Client* c, QRect moveResizeGeom, int mode )
412 //adapted from adjustClientPosition on 29May2004
413 //this function is called when resizing a window and will modify
414 //the new dimensions to snap to other windows/borders if appropriate
415 if ( options->windowSnapZone || options->borderSnapZone )
417 const bool sOWO=options->snapOnlyWhenOverlapping;
419 const QRect maxRect = clientArea(MovementArea, c->rect().center(), c->desktop());
420 const int xmin = maxRect.left();
421 const int xmax = maxRect.right(); //desk size
422 const int ymin = maxRect.top();
423 const int ymax = maxRect.bottom();
425 const int cx(moveResizeGeom.left());
426 const int cy(moveResizeGeom.top());
427 const int rx(moveResizeGeom.right());
428 const int ry(moveResizeGeom.bottom());
430 int newcx(cx), newcy(cy); //buffers
431 int newrx(rx), newry(ry);
432 int deltaX(xmax);
433 int deltaY(ymax); //minimum distance to other clients
435 int lx, ly, lrx, lry; //coords and size for the comparison client, l
437 // border snap
438 int snap = options->borderSnapZone; //snap trigger
439 if (snap)
441 deltaX = int(snap);
442 deltaY = int(snap);
444 #define SNAP_BORDER_TOP \
445 if ((sOWO?(newcy<ymin):true) && (qAbs(ymin-newcy)<deltaY)) \
447 deltaY = qAbs(ymin-newcy); \
448 newcy = ymin; \
451 #define SNAP_BORDER_BOTTOM \
452 if ((sOWO?(newry>ymax):true) && (qAbs(ymax-newry)<deltaY)) \
454 deltaY = qAbs(ymax-newcy); \
455 newry = ymax; \
458 #define SNAP_BORDER_LEFT \
459 if ((sOWO?(newcx<xmin):true) && (qAbs(xmin-newcx)<deltaX)) \
461 deltaX = qAbs(xmin-newcx); \
462 newcx = xmin; \
465 #define SNAP_BORDER_RIGHT \
466 if ((sOWO?(newrx>xmax):true) && (qAbs(xmax-newrx)<deltaX)) \
468 deltaX = qAbs(xmax-newrx); \
469 newrx = xmax; \
471 switch ( mode )
473 case PositionBottomRight:
474 SNAP_BORDER_BOTTOM
475 SNAP_BORDER_RIGHT
476 break;
477 case PositionRight:
478 SNAP_BORDER_RIGHT
479 break;
480 case PositionBottom:
481 SNAP_BORDER_BOTTOM
482 break;
483 case PositionTopLeft:
484 SNAP_BORDER_TOP
485 SNAP_BORDER_LEFT
486 break;
487 case PositionLeft:
488 SNAP_BORDER_LEFT
489 break;
490 case PositionTop:
491 SNAP_BORDER_TOP
492 break;
493 case PositionTopRight:
494 SNAP_BORDER_TOP
495 SNAP_BORDER_RIGHT
496 break;
497 case PositionBottomLeft:
498 SNAP_BORDER_BOTTOM
499 SNAP_BORDER_LEFT
500 break;
501 default:
502 assert( false );
503 break;
509 // windows snap
510 snap = options->windowSnapZone;
511 if (snap)
513 deltaX = int(snap);
514 deltaY = int(snap);
515 QList<Client *>::ConstIterator l;
516 for (l = clients.begin();l != clients.end();++l )
518 if ((*l)->isOnDesktop(currentDesktop()) &&
519 !(*l)->isMinimized()
520 && (*l) != c )
522 lx = (*l)->x()-1;
523 ly = (*l)->y()-1;
524 lrx =(*l)->x() + (*l)->width();
525 lry =(*l)->y() + (*l)->height();
527 #define WITHIN_HEIGHT ((( newcy <= lry ) && ( newcy >= ly )) || \
528 (( newry >= ly ) && ( newry <= lry )) || \
529 (( newcy <= ly ) && ( newry >= lry )) )
531 #define WITHIN_WIDTH ( (( cx <= lrx ) && ( cx >= lx )) || \
532 (( rx >= lx ) && ( rx <= lrx )) || \
533 (( cx <= lx ) && ( rx >= lrx )) )
535 #define SNAP_WINDOW_TOP if ( (sOWO?(newcy<lry):true) \
536 && WITHIN_WIDTH \
537 && (qAbs( lry - newcy ) < deltaY) ) { \
538 deltaY = qAbs( lry - newcy ); \
539 newcy=lry; \
542 #define SNAP_WINDOW_BOTTOM if ( (sOWO?(newry>ly):true) \
543 && WITHIN_WIDTH \
544 && (qAbs( ly - newry ) < deltaY) ) { \
545 deltaY = qAbs( ly - newry ); \
546 newry=ly; \
549 #define SNAP_WINDOW_LEFT if ( (sOWO?(newcx<lrx):true) \
550 && WITHIN_HEIGHT \
551 && (qAbs( lrx - newcx ) < deltaX)) { \
552 deltaX = qAbs( lrx - newcx ); \
553 newcx=lrx; \
556 #define SNAP_WINDOW_RIGHT if ( (sOWO?(newrx>lx):true) \
557 && WITHIN_HEIGHT \
558 && (qAbs( lx - newrx ) < deltaX)) \
560 deltaX = qAbs( lx - newrx ); \
561 newrx=lx; \
564 switch ( mode )
566 case PositionBottomRight:
567 SNAP_WINDOW_BOTTOM
568 SNAP_WINDOW_RIGHT
569 break;
570 case PositionRight:
571 SNAP_WINDOW_RIGHT
572 break;
573 case PositionBottom:
574 SNAP_WINDOW_BOTTOM
575 break;
576 case PositionTopLeft:
577 SNAP_WINDOW_TOP
578 SNAP_WINDOW_LEFT
579 break;
580 case PositionLeft:
581 SNAP_WINDOW_LEFT
582 break;
583 case PositionTop:
584 SNAP_WINDOW_TOP
585 break;
586 case PositionTopRight:
587 SNAP_WINDOW_TOP
588 SNAP_WINDOW_RIGHT
589 break;
590 case PositionBottomLeft:
591 SNAP_WINDOW_BOTTOM
592 SNAP_WINDOW_LEFT
593 break;
594 default:
595 assert( false );
596 break;
601 moveResizeGeom = QRect(QPoint(newcx, newcy), QPoint(newrx, newry));
603 return moveResizeGeom;
607 Marks the client as being moved around by the user.
609 void Workspace::setClientIsMoving( Client *c )
611 Q_ASSERT(!c || !movingClient); // Catch attempts to move a second
612 // window while still moving the first one.
613 movingClient = c;
614 if (movingClient)
615 ++block_focus;
616 else
617 --block_focus;
621 Cascades all clients on the current desktop
623 void Workspace::cascadeDesktop()
625 // TODO XINERAMA this probably is not right for xinerama
626 Q_ASSERT( block_stacking_updates == 0 );
627 ClientList::ConstIterator it(stackingOrder().begin());
628 initPositioning->reinitCascading( currentDesktop());
629 QRect area = clientArea( PlacementArea, QPoint( 0, 0 ), currentDesktop());
630 for (; it != stackingOrder().end(); ++it)
632 if((!(*it)->isOnDesktop(currentDesktop())) ||
633 ((*it)->isMinimized()) ||
634 ((*it)->isOnAllDesktops()) ||
635 (!(*it)->isMovable()) )
636 continue;
637 initPositioning->placeCascaded(*it, area);
642 Unclutters the current desktop by smart-placing all clients
643 again.
645 void Workspace::unclutterDesktop()
647 for ( int i = clients.size() - 1; i>=0; i-- )
649 if( ( !clients.at( i )->isOnDesktop( currentDesktop() ) ) ||
650 (clients.at( i )->isMinimized()) ||
651 (clients.at( i )->isOnAllDesktops()) ||
652 (!clients.at( i )->isMovable()) )
653 continue;
654 initPositioning->placeSmart(clients.at( i ), QRect());
659 void Workspace::updateTopMenuGeometry( Client* c )
661 if( !managingTopMenus())
662 return;
663 if( c != NULL )
665 XEvent ev;
666 ev.xclient.display = display();
667 ev.xclient.type = ClientMessage;
668 ev.xclient.window = c->window();
669 static Atom msg_type_atom = XInternAtom( display(), "_KDE_TOPMENU_MINSIZE", False );
670 ev.xclient.message_type = msg_type_atom;
671 ev.xclient.format = 32;
672 ev.xclient.data.l[0] = xTime();
673 ev.xclient.data.l[1] = topmenu_space->width();
674 ev.xclient.data.l[2] = topmenu_space->height();
675 ev.xclient.data.l[3] = 0;
676 ev.xclient.data.l[4] = 0;
677 XSendEvent( display(), c->window(), False, NoEventMask, &ev );
678 KWindowSystem::setStrut( c->window(), 0, 0, topmenu_height, 0 ); // so that kicker etc. know
679 c->checkWorkspacePosition();
680 return;
682 // c == NULL - update all, including topmenu_space
683 QRect area;
684 area = clientArea( MaximizeFullArea, QPoint( 0, 0 ), 1 ); // HACK desktop ?
685 area.setHeight( topMenuHeight());
686 topmenu_space->setGeometry( area );
687 for( ClientList::ConstIterator it = topmenus.begin();
688 it != topmenus.end();
689 ++it )
690 updateTopMenuGeometry( *it );
693 //********************************************
694 // Client
695 //********************************************
698 void Client::keepInArea( QRect area, bool partial )
700 if( partial )
702 // increase the area so that can have only 100 pixels in the area
703 area.setLeft( qMin( area.left() - width() + 100, area.left()));
704 area.setTop( qMin( area.top() - height() + 100, area.top()));
705 area.setRight( qMax( area.right() + width() - 100, area.right()));
706 area.setBottom( qMax( area.bottom() + height() - 100, area.bottom()));
708 if ( geometry().right() > area.right() && width() < area.width() )
709 move( area.right() - width(), y() );
710 if ( geometry().bottom() > area.bottom() && height() < area.height() )
711 move( x(), area.bottom() - height() );
712 if( !area.contains( geometry().topLeft() ))
714 int tx = x();
715 int ty = y();
716 if ( tx < area.x() )
717 tx = area.x();
718 if ( ty < area.y() )
719 ty = area.y();
720 move( tx, ty );
725 Returns \a area with the client's strut taken into account.
727 Used from Workspace in updateClientArea.
729 // TODO move to Workspace?
731 QRect Client::adjustedClientArea( const QRect &desktopArea, const QRect& area ) const
733 QRect r = area;
734 // topmenu area is reserved in updateClientArea()
735 if( isTopMenu())
736 return r;
737 NETExtendedStrut str = strut();
738 QRect stareaL = QRect(
740 str . left_start,
741 str . left_width,
742 str . left_end - str . left_start + 1 );
743 QRect stareaR = QRect (
744 desktopArea . right () - str . right_width + 1,
745 str . right_start,
746 str . right_width,
747 str . right_end - str . right_start + 1 );
748 QRect stareaT = QRect (
749 str . top_start,
751 str . top_end - str . top_start + 1,
752 str . top_width);
753 QRect stareaB = QRect (
754 str . bottom_start,
755 desktopArea . bottom () - str . bottom_width + 1,
756 str . bottom_end - str . bottom_start + 1,
757 str . bottom_width);
759 QRect screenarea = workspace()->clientArea( ScreenArea, this );
760 // HACK: workarea handling is not xinerama aware, so if this strut
761 // reserves place at a xinerama edge that's inside the virtual screen,
762 // ignore the strut for workspace setting.
763 if( area == kapp->desktop()->geometry())
765 if( stareaL.left() < screenarea.left())
766 stareaL = QRect();
767 if( stareaR.right() > screenarea.right())
768 stareaR = QRect();
769 if( stareaT.top() < screenarea.top())
770 stareaT = QRect();
771 if( stareaB.bottom() < screenarea.bottom())
772 stareaB = QRect();
774 // Handle struts at xinerama edges that are inside the virtual screen.
775 // They're given in virtual screen coordinates, make them affect only
776 // their xinerama screen.
777 stareaL.setLeft( qMax( stareaL.left(), screenarea.left()));
778 stareaR.setRight( qMin( stareaR.right(), screenarea.right()));
779 stareaT.setTop( qMax( stareaT.top(), screenarea.top()));
780 stareaB.setBottom( qMin( stareaB.bottom(), screenarea.bottom()));
782 if (stareaL . intersects (area)) {
783 // kDebug () << "Moving left of: " << r << " to " << stareaL.right() + 1;
784 r . setLeft( stareaL . right() + 1 );
786 if (stareaR . intersects (area)) {
787 // kDebug () << "Moving right of: " << r << " to " << stareaR.left() - 1;
788 r . setRight( stareaR . left() - 1 );
790 if (stareaT . intersects (area)) {
791 // kDebug () << "Moving top of: " << r << " to " << stareaT.bottom() + 1;
792 r . setTop( stareaT . bottom() + 1 );
794 if (stareaB . intersects (area)) {
795 // kDebug () << "Moving bottom of: " << r << " to " << stareaB.top() - 1;
796 r . setBottom( stareaB . top() - 1 );
798 return r;
801 NETExtendedStrut Client::strut() const
803 NETExtendedStrut ext = info->extendedStrut();
804 NETStrut str = info->strut();
805 if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0
806 && ( str.left != 0 || str.right != 0 || str.top != 0 || str.bottom != 0 ))
808 // build extended from simple
809 if( str.left != 0 )
811 ext.left_width = str.left;
812 ext.left_start = 0;
813 ext.left_end = displayHeight();
815 if( str.right != 0 )
817 ext.right_width = str.right;
818 ext.right_start = 0;
819 ext.right_end = displayHeight();
821 if( str.top != 0 )
823 ext.top_width = str.top;
824 ext.top_start = 0;
825 ext.top_end = displayWidth();
827 if( str.bottom != 0 )
829 ext.bottom_width = str.bottom;
830 ext.bottom_start = 0;
831 ext.bottom_end = displayWidth();
834 return ext;
837 bool Client::hasStrut() const
839 NETExtendedStrut ext = strut();
840 if( ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0 )
841 return false;
842 return true;
846 // updates differences to workarea edges for all directions
847 void Client::updateWorkareaDiffs()
849 QRect area = workspace()->clientArea( WorkArea, this );
850 QRect geom = geometry();
851 workarea_diff_x = computeWorkareaDiff( geom.left(), geom.right(), area.left(), area.right());
852 workarea_diff_y = computeWorkareaDiff( geom.top(), geom.bottom(), area.top(), area.bottom());
855 // If the client was inside workarea in the x direction, and if it was close to the left/right
856 // edge, return the distance from the left/right edge (negative for left, positive for right)
857 // INT_MIN means 'not inside workarea', INT_MAX means 'not near edge'.
858 // In order to recognize 'at the left workarea edge' from 'at the right workarea edge'
859 // (i.e. negative vs positive zero), the distances are one larger in absolute value than they
860 // really are (i.e. 5 pixels from the left edge is -6, not -5). A bit hacky, but I'm lazy
861 // to rewrite it just to make it nicer. If this will ever get touched again, perhaps then.
862 // the y direction is done the same, just the values will be rotated: top->left, bottom->right
863 int Client::computeWorkareaDiff( int left, int right, int a_left, int a_right )
865 int left_diff = left - a_left;
866 int right_diff = a_right - right;
867 if( left_diff < 0 || right_diff < 0 )
868 return INT_MIN;
869 else // fully inside workarea in this direction direction
871 // max distance from edge where it's still considered to be close and is kept at that distance
872 int max_diff = ( a_right - a_left ) / 10;
873 if( left_diff < right_diff )
874 return left_diff < max_diff ? -left_diff - 1 : INT_MAX;
875 else if( left_diff > right_diff )
876 return right_diff < max_diff ? right_diff + 1 : INT_MAX;
877 return INT_MAX; // not close to workarea edge
881 void Client::checkWorkspacePosition()
883 if( isDesktop())
885 QRect area = workspace()->clientArea( FullArea, this );
886 if( geometry() != area )
887 setGeometry( area );
888 return;
890 if( isFullScreen())
892 QRect area = workspace()->clientArea( FullScreenArea, this );
893 if( geometry() != area )
894 setGeometry( area );
895 return;
897 if( isDock())
898 return;
899 if( isTopMenu())
901 if( workspace()->managingTopMenus())
903 QRect area;
904 ClientList mainclients = mainClients();
905 if( mainclients.count() == 1 )
906 area = workspace()->clientArea( MaximizeFullArea, mainclients.first());
907 else
908 area = workspace()->clientArea( MaximizeFullArea, QPoint( 0, 0 ), desktop());
909 area.setHeight( workspace()->topMenuHeight());
910 // kDebug() << "TOPMENU size adjust: " << area << ":" << this;
911 setGeometry( area );
913 return;
916 if( maximizeMode() != MaximizeRestore )
917 // TODO update geom_restore?
918 changeMaximize( false, false, true ); // adjust size
920 if( !isShade()) // TODO
922 int old_diff_x = workarea_diff_x;
923 int old_diff_y = workarea_diff_y;
924 updateWorkareaDiffs();
926 // this can be true only if this window was mapped before KWin
927 // was started - in such case, don't adjust position to workarea,
928 // because the window already had its position, and if a window
929 // with a strut altering the workarea would be managed in initialization
930 // after this one, this window would be moved
931 if( workspace()->initializing())
932 return;
934 QRect area = workspace()->clientArea( WorkArea, this );
935 QRect new_geom = geometry();
936 QRect tmp_rect_x( new_geom.left(), 0, new_geom.width(), 0 );
937 QRect tmp_area_x( area.left(), 0, area.width(), 0 );
938 checkDirection( workarea_diff_x, old_diff_x, tmp_rect_x, tmp_area_x );
939 // the x<->y swapping
940 QRect tmp_rect_y( new_geom.top(), 0, new_geom.height(), 0 );
941 QRect tmp_area_y( area.top(), 0, area.height(), 0 );
942 checkDirection( workarea_diff_y, old_diff_y, tmp_rect_y, tmp_area_y );
943 new_geom = QRect( tmp_rect_x.left(), tmp_rect_y.left(), tmp_rect_x.width(), tmp_rect_y.width());
944 QRect final_geom( new_geom.topLeft(), adjustedSize( new_geom.size()));
945 if( final_geom != new_geom ) // size increments, or size restrictions
946 { // adjusted size differing matters only for right and bottom edge
947 if( old_diff_x != INT_MAX && old_diff_x > 0 )
948 final_geom.moveRight( area.right() - ( old_diff_x - 1 ));
949 if( old_diff_y != INT_MAX && old_diff_y > 0 )
950 final_geom.moveBottom( area.bottom() - ( old_diff_y - 1 ));
952 if( final_geom != geometry() )
953 setGeometry( final_geom );
954 // updateWorkareaDiffs(); done already by setGeometry()
958 // Try to be smart about keeping the clients visible.
959 // If the client was fully inside the workspace before, try to keep
960 // it still inside the workarea, possibly moving it or making it smaller if possible,
961 // and try to keep the distance from the nearest workarea edge.
962 // On the other hand, it it was partially moved outside of the workspace in some direction,
963 // don't do anything with that direction if it's still at least partially visible. If it's
964 // not visible anymore at all, make sure it's visible at least partially
965 // again (not fully, as that could(?) be potentionally annoying) by
966 // moving it slightly inside the workarea (those '+ 5').
967 // Again, this is done for the x direction, y direction will be done by x<->y swapping
968 void Client::checkDirection( int new_diff, int old_diff, QRect& rect, const QRect& area )
970 if( old_diff != INT_MIN ) // was inside workarea
972 if( old_diff == INT_MAX ) // was in workarea, but far from edge
974 if( new_diff == INT_MIN ) // is not anymore fully in workarea
976 rect.setLeft( area.left());
977 rect.setRight( area.right());
979 return;
981 if( isMovable())
983 if( old_diff < 0 ) // was in left third, keep distance from left edge
984 rect.moveLeft( area.left() + ( -old_diff - 1 ));
985 else // old_diff > 0 // was in right third, keep distance from right edge
986 rect.moveRight( area.right() - ( old_diff - 1 ));
988 else if( isResizable())
990 if( old_diff < 0 )
991 rect.setLeft( area.left() + ( -old_diff - 1 ) );
992 else // old_diff > 0
993 rect.setRight( area.right() - ( old_diff - 1 ));
995 if( rect.width() > area.width() && isResizable())
996 rect.setWidth( area.width());
997 if( isMovable())
999 if( rect.left() < area.left())
1000 rect.moveLeft( area.left());
1001 else if( rect.right() > area.right())
1002 rect.moveRight( area.right());
1005 if( rect.right() < area.left() + 5 || rect.left() > area.right() - 5 )
1006 { // not visible (almost) at all - try to make it at least partially visible
1007 if( isMovable())
1009 if( rect.left() < area.left() + 5 )
1010 rect.moveRight( area.left() + 5 );
1011 if( rect.right() > area.right() - 5 )
1012 rect.moveLeft( area.right() - 5 );
1018 Adjust the frame size \a frame according to he window's size hints.
1020 QSize Client::adjustedSize( const QSize& frame, Sizemode mode ) const
1022 // first, get the window size for the given frame size s
1024 QSize wsize( frame.width() - ( border_left + border_right ),
1025 frame.height() - ( border_top + border_bottom ));
1026 if( wsize.isEmpty())
1027 wsize = QSize( 1, 1 );
1029 return sizeForClientSize( wsize, mode, false );
1032 // this helper returns proper size even if the window is shaded
1033 // see also the comment in Client::setGeometry()
1034 QSize Client::adjustedSize() const
1036 return sizeForClientSize( clientSize());
1040 Calculate the appropriate frame size for the given client size \a
1041 wsize.
1043 \a wsize is adapted according to the window's size hints (minimum,
1044 maximum and incremental size changes).
1047 QSize Client::sizeForClientSize( const QSize& wsize, Sizemode mode, bool noframe ) const
1049 int w = wsize.width();
1050 int h = wsize.height();
1051 if( w < 1 || h < 1 )
1053 kWarning() << "sizeForClientSize() with empty size!" ;
1054 kWarning() << kBacktrace() ;
1056 if (w<1) w = 1;
1057 if (h<1) h = 1;
1059 // basesize, minsize, maxsize, paspect and resizeinc have all values defined,
1060 // even if they're not set in flags - see getWmNormalHints()
1061 QSize min_size = minSize();
1062 QSize max_size = maxSize();
1063 if( decoration != NULL )
1065 QSize decominsize = decoration->minimumSize();
1066 QSize border_size( border_left + border_right, border_top + border_bottom );
1067 if( border_size.width() > decominsize.width()) // just in case
1068 decominsize.setWidth( border_size.width());
1069 if( border_size.height() > decominsize.height())
1070 decominsize.setHeight( border_size.height());
1071 if( decominsize.width() > min_size.width())
1072 min_size.setWidth( decominsize.width());
1073 if( decominsize.height() > min_size.height())
1074 min_size.setHeight( decominsize.height());
1076 w = qMin( max_size.width(), w );
1077 h = qMin( max_size.height(), h );
1078 w = qMax( min_size.width(), w );
1079 h = qMax( min_size.height(), h );
1081 int w1 = w;
1082 int h1 = h;
1083 int width_inc = xSizeHint.width_inc;
1084 int height_inc = xSizeHint.height_inc;
1085 int basew_inc = xSizeHint.min_width; // see getWmNormalHints()
1086 int baseh_inc = xSizeHint.min_height;
1087 w = int(( w - basew_inc ) / width_inc ) * width_inc + basew_inc;
1088 h = int(( h - baseh_inc ) / height_inc ) * height_inc + baseh_inc;
1089 // code for aspect ratios based on code from FVWM
1091 * The math looks like this:
1093 * minAspectX dwidth maxAspectX
1094 * ---------- <= ------- <= ----------
1095 * minAspectY dheight maxAspectY
1097 * If that is multiplied out, then the width and height are
1098 * invalid in the following situations:
1100 * minAspectX * dheight > minAspectY * dwidth
1101 * maxAspectX * dheight < maxAspectY * dwidth
1104 if( xSizeHint.flags & PAspect )
1106 double min_aspect_w = xSizeHint.min_aspect.x; // use doubles, because the values can be MAX_INT
1107 double min_aspect_h = xSizeHint.min_aspect.y; // and multiplying would go wrong otherwise
1108 double max_aspect_w = xSizeHint.max_aspect.x;
1109 double max_aspect_h = xSizeHint.max_aspect.y;
1110 // According to ICCCM 4.1.2.3 PMinSize should be a fallback for PBaseSize for size increments,
1111 // but not for aspect ratio. Since this code comes from FVWM, handles both at the same time,
1112 // and I have no idea how it works, let's hope nobody relies on that.
1113 w -= xSizeHint.base_width;
1114 h -= xSizeHint.base_height;
1115 int max_width = max_size.width() - xSizeHint.base_width;
1116 int min_width = min_size.width() - xSizeHint.base_width;
1117 int max_height = max_size.height() - xSizeHint.base_height;
1118 int min_height = min_size.height() - xSizeHint.base_height;
1119 #define ASPECT_CHECK_GROW_W \
1120 if( min_aspect_w * h > min_aspect_h * w ) \
1122 int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
1123 if( w + delta <= max_width ) \
1124 w += delta; \
1126 #define ASPECT_CHECK_SHRINK_H_GROW_W \
1127 if( min_aspect_w * h > min_aspect_h * w ) \
1129 int delta = int( h - w * min_aspect_h / min_aspect_w ) / height_inc * height_inc; \
1130 if( h - delta >= min_height ) \
1131 h -= delta; \
1132 else \
1134 int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
1135 if( w + delta <= max_width ) \
1136 w += delta; \
1139 #define ASPECT_CHECK_GROW_H \
1140 if( max_aspect_w * h < max_aspect_h * w ) \
1142 int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
1143 if( h + delta <= max_height ) \
1144 h += delta; \
1146 #define ASPECT_CHECK_SHRINK_W_GROW_H \
1147 if( max_aspect_w * h < max_aspect_h * w ) \
1149 int delta = int( w - max_aspect_w * h / max_aspect_h ) / width_inc * width_inc; \
1150 if( w - delta >= min_width ) \
1151 w -= delta; \
1152 else \
1154 int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
1155 if( h + delta <= max_height ) \
1156 h += delta; \
1159 switch( mode )
1161 case SizemodeAny:
1162 #if 0 // make SizemodeAny equal to SizemodeFixedW - prefer keeping fixed width,
1163 // so that changing aspect ratio to a different value and back keeps the same size (#87298)
1165 ASPECT_CHECK_SHRINK_H_GROW_W
1166 ASPECT_CHECK_SHRINK_W_GROW_H
1167 ASPECT_CHECK_GROW_H
1168 ASPECT_CHECK_GROW_W
1169 break;
1171 #endif
1172 case SizemodeFixedW:
1174 // the checks are order so that attempts to modify height are first
1175 ASPECT_CHECK_GROW_H
1176 ASPECT_CHECK_SHRINK_H_GROW_W
1177 ASPECT_CHECK_SHRINK_W_GROW_H
1178 ASPECT_CHECK_GROW_W
1179 break;
1181 case SizemodeFixedH:
1183 ASPECT_CHECK_GROW_W
1184 ASPECT_CHECK_SHRINK_W_GROW_H
1185 ASPECT_CHECK_SHRINK_H_GROW_W
1186 ASPECT_CHECK_GROW_H
1187 break;
1189 case SizemodeMax:
1191 // first checks that try to shrink
1192 ASPECT_CHECK_SHRINK_H_GROW_W
1193 ASPECT_CHECK_SHRINK_W_GROW_H
1194 ASPECT_CHECK_GROW_W
1195 ASPECT_CHECK_GROW_H
1196 break;
1199 #undef ASPECT_CHECK_SHRINK_H_GROW_W
1200 #undef ASPECT_CHECK_SHRINK_W_GROW_H
1201 #undef ASPECT_CHECK_GROW_W
1202 #undef ASPECT_CHECK_GROW_H
1203 w += xSizeHint.base_width;
1204 h += xSizeHint.base_height;
1206 if( !rules()->checkStrictGeometry( false ))
1208 // disobey increments and aspect when maximized
1209 if( maximizeMode() & MaximizeHorizontal )
1210 w = w1;
1211 if( maximizeMode() & MaximizeVertical )
1212 h = h1;
1215 if( !noframe )
1217 w += border_left + border_right;
1218 h += border_top + border_bottom;
1220 return rules()->checkSize( QSize( w, h ));
1224 Gets the client's normal WM hints and reconfigures itself respectively.
1226 void Client::getWmNormalHints()
1228 long msize;
1229 if (XGetWMNormalHints(display(), window(), &xSizeHint, &msize) == 0 )
1230 xSizeHint.flags = 0;
1231 // set defined values for the fields, even if they're not in flags
1233 if( ! ( xSizeHint.flags & PMinSize ))
1234 xSizeHint.min_width = xSizeHint.min_height = 0;
1235 if( xSizeHint.flags & PBaseSize )
1237 // PBaseSize is a fallback for PMinSize according to ICCCM 4.1.2.3
1238 // The other way around PMinSize is not a complete fallback for PBaseSize,
1239 // so that's not handled here.
1240 if( ! ( xSizeHint.flags & PMinSize ))
1242 xSizeHint.min_width = xSizeHint.base_width;
1243 xSizeHint.min_height = xSizeHint.base_height;
1246 else
1247 xSizeHint.base_width = xSizeHint.base_height = 0;
1248 if( ! ( xSizeHint.flags & PMaxSize ))
1249 xSizeHint.max_width = xSizeHint.max_height = INT_MAX;
1250 else
1252 xSizeHint.max_width = qMax( xSizeHint.max_width, 1 );
1253 xSizeHint.max_height = qMax( xSizeHint.max_height, 1 );
1255 if( xSizeHint.flags & PResizeInc )
1257 xSizeHint.width_inc = qMax( xSizeHint.width_inc, 1 );
1258 xSizeHint.height_inc = qMax( xSizeHint.height_inc, 1 );
1260 else
1262 xSizeHint.width_inc = 1;
1263 xSizeHint.height_inc = 1;
1265 if( xSizeHint.flags & PAspect )
1266 { // no dividing by zero
1267 xSizeHint.min_aspect.y = qMax( xSizeHint.min_aspect.y, 1 );
1268 xSizeHint.max_aspect.y = qMax( xSizeHint.max_aspect.y, 1 );
1270 else
1272 xSizeHint.min_aspect.x = 1;
1273 xSizeHint.min_aspect.y = INT_MAX;
1274 xSizeHint.max_aspect.x = INT_MAX;
1275 xSizeHint.max_aspect.y = 1;
1277 if( ! ( xSizeHint.flags & PWinGravity ))
1278 xSizeHint.win_gravity = NorthWestGravity;
1279 if( isManaged())
1280 { // update to match restrictions
1281 QSize new_size = adjustedSize();
1282 if( new_size != size() && !isFullScreen())
1284 QRect orig_geometry = geometry();
1285 resizeWithChecks( new_size );
1286 if( ( !isSpecialWindow() || isToolbar()) && !isFullScreen())
1288 // try to keep the window in its xinerama screen if possible,
1289 // if that fails at least keep it visible somewhere
1290 QRect area = workspace()->clientArea( MovementArea, this );
1291 if( area.contains( orig_geometry ))
1292 keepInArea( area );
1293 area = workspace()->clientArea( WorkArea, this );
1294 if( area.contains( orig_geometry ))
1295 keepInArea( area );
1299 updateAllowedActions(); // affects isResizeable()
1302 QSize Client::minSize() const
1304 return rules()->checkMinSize( QSize( xSizeHint.min_width, xSizeHint.min_height ));
1307 QSize Client::maxSize() const
1309 return rules()->checkMaxSize( QSize( xSizeHint.max_width, xSizeHint.max_height ));
1313 Auxiliary function to inform the client about the current window
1314 configuration.
1317 void Client::sendSyntheticConfigureNotify()
1319 XConfigureEvent c;
1320 c.type = ConfigureNotify;
1321 c.send_event = True;
1322 c.event = window();
1323 c.window = window();
1324 c.x = x() + clientPos().x();
1325 c.y = y() + clientPos().y();
1326 c.width = clientSize().width();
1327 c.height = clientSize().height();
1328 c.border_width = 0;
1329 c.above = None;
1330 c.override_redirect = 0;
1331 XSendEvent( display(), c.event, true, StructureNotifyMask, (XEvent*)&c );
1334 const QPoint Client::calculateGravitation( bool invert, int gravity ) const
1336 int dx, dy;
1337 dx = dy = 0;
1339 if( gravity == 0 ) // default (nonsense) value for the argument
1340 gravity = xSizeHint.win_gravity;
1342 // dx, dy specify how the client window moves to make space for the frame
1343 switch (gravity)
1345 case NorthWestGravity: // move down right
1346 default:
1347 dx = border_left;
1348 dy = border_top;
1349 break;
1350 case NorthGravity: // move right
1351 dx = 0;
1352 dy = border_top;
1353 break;
1354 case NorthEastGravity: // move down left
1355 dx = -border_right;
1356 dy = border_top;
1357 break;
1358 case WestGravity: // move right
1359 dx = border_left;
1360 dy = 0;
1361 break;
1362 case CenterGravity:
1363 break; // will be handled specially
1364 case StaticGravity: // don't move
1365 dx = 0;
1366 dy = 0;
1367 break;
1368 case EastGravity: // move left
1369 dx = -border_right;
1370 dy = 0;
1371 break;
1372 case SouthWestGravity: // move up right
1373 dx = border_left ;
1374 dy = -border_bottom;
1375 break;
1376 case SouthGravity: // move up
1377 dx = 0;
1378 dy = -border_bottom;
1379 break;
1380 case SouthEastGravity: // move up left
1381 dx = -border_right;
1382 dy = -border_bottom;
1383 break;
1385 if( gravity != CenterGravity )
1386 { // translate from client movement to frame movement
1387 dx -= border_left;
1388 dy -= border_top;
1390 else
1391 { // center of the frame will be at the same position client center without frame would be
1392 dx = - ( border_left + border_right ) / 2;
1393 dy = - ( border_top + border_bottom ) / 2;
1395 if( !invert )
1396 return QPoint( x() + dx, y() + dy );
1397 else
1398 return QPoint( x() - dx, y() - dy );
1401 void Client::configureRequest( int value_mask, int rx, int ry, int rw, int rh, int gravity, bool from_tool )
1403 if( gravity == 0 ) // default (nonsense) value for the argument
1404 gravity = xSizeHint.win_gravity;
1405 if( value_mask & ( CWX | CWY ))
1407 QPoint new_pos = calculateGravitation( true, gravity ); // undo gravitation
1408 if ( value_mask & CWX )
1409 new_pos.setX( rx );
1410 if ( value_mask & CWY )
1411 new_pos.setY( ry );
1413 // clever(?) workaround for applications like xv that want to set
1414 // the location to the current location but miscalculate the
1415 // frame size due to kwin being a double-reparenting window
1416 // manager
1417 if ( new_pos.x() == x() + clientPos().x() && new_pos.y() == y() + clientPos().y()
1418 && gravity == NorthWestGravity && !from_tool )
1420 new_pos.setX( x());
1421 new_pos.setY( y());
1424 int nw = clientSize().width();
1425 int nh = clientSize().height();
1426 if ( value_mask & CWWidth )
1427 nw = rw;
1428 if ( value_mask & CWHeight )
1429 nh = rh;
1430 QSize ns = sizeForClientSize( QSize( nw, nh ) ); // enforces size if needed
1431 new_pos = rules()->checkPosition( new_pos );
1433 // TODO what to do with maximized windows?
1434 if ( maximizeMode() != MaximizeFull
1435 || ns != size())
1437 QRect orig_geometry = geometry();
1438 GeometryUpdatesBlocker blocker( this );
1439 move( new_pos );
1440 plainResize( ns );
1441 setGeometry( QRect( calculateGravitation( false, gravity ), size()));
1442 updateFullScreenHack( QRect( new_pos, QSize( nw, nh )));
1443 QRect area = workspace()->clientArea( WorkArea, this );
1444 if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen()
1445 && area.contains( orig_geometry ))
1446 keepInArea( area );
1448 // this is part of the kicker-xinerama-hack... it should be
1449 // safe to remove when kicker gets proper ExtendedStrut support;
1450 // see Workspace::updateClientArea() and
1451 // Client::adjustedClientArea()
1452 if (hasStrut ())
1453 workspace() -> updateClientArea ();
1457 if ( value_mask & (CWWidth | CWHeight )
1458 && ! ( value_mask & ( CWX | CWY )) ) // pure resize
1460 int nw = clientSize().width();
1461 int nh = clientSize().height();
1462 if ( value_mask & CWWidth )
1463 nw = rw;
1464 if ( value_mask & CWHeight )
1465 nh = rh;
1466 QSize ns = sizeForClientSize( QSize( nw, nh ) );
1468 if( ns != size()) // don't restore if some app sets its own size again
1470 QRect orig_geometry = geometry();
1471 GeometryUpdatesBlocker blocker( this );
1472 int save_gravity = xSizeHint.win_gravity;
1473 xSizeHint.win_gravity = gravity;
1474 resizeWithChecks( ns );
1475 xSizeHint.win_gravity = save_gravity;
1476 updateFullScreenHack( QRect( calculateGravitation( true, xSizeHint.win_gravity ), QSize( nw, nh )));
1477 if( !from_tool && ( !isSpecialWindow() || isToolbar()) && !isFullScreen())
1479 // try to keep the window in its xinerama screen if possible,
1480 // if that fails at least keep it visible somewhere
1481 QRect area = workspace()->clientArea( MovementArea, this );
1482 if( area.contains( orig_geometry ))
1483 keepInArea( area );
1484 area = workspace()->clientArea( WorkArea, this );
1485 if( area.contains( orig_geometry ))
1486 keepInArea( area );
1490 // No need to send synthetic configure notify event here, either it's sent together
1491 // with geometry change, or there's no need to send it.
1492 // Handling of the real ConfigureRequest event forces sending it, as there it's necessary.
1495 void Client::resizeWithChecks( int w, int h, ForceGeometry_t force )
1497 if( shade_geometry_change )
1498 assert( false );
1499 else if( isShade())
1501 if( h == border_top + border_bottom )
1503 kWarning() << "Shaded geometry passed for size:" ;
1504 kWarning() << kBacktrace() ;
1507 int newx = x();
1508 int newy = y();
1509 QRect area = workspace()->clientArea( WorkArea, this );
1510 // don't allow growing larger than workarea
1511 if( w > area.width())
1512 w = area.width();
1513 if( h > area.height())
1514 h = area.height();
1515 QSize tmp = adjustedSize( QSize( w, h )); // checks size constraints, including min/max size
1516 w = tmp.width();
1517 h = tmp.height();
1518 switch( xSizeHint.win_gravity )
1520 case NorthWestGravity: // top left corner doesn't move
1521 default:
1522 break;
1523 case NorthGravity: // middle of top border doesn't move
1524 newx = ( newx + width() / 2 ) - ( w / 2 );
1525 break;
1526 case NorthEastGravity: // top right corner doesn't move
1527 newx = newx + width() - w;
1528 break;
1529 case WestGravity: // middle of left border doesn't move
1530 newy = ( newy + height() / 2 ) - ( h / 2 );
1531 break;
1532 case CenterGravity: // middle point doesn't move
1533 newx = ( newx + width() / 2 ) - ( w / 2 );
1534 newy = ( newy + height() / 2 ) - ( h / 2 );
1535 break;
1536 case StaticGravity: // top left corner of _client_ window doesn't move
1537 // since decoration doesn't change, equal to NorthWestGravity
1538 break;
1539 case EastGravity: // // middle of right border doesn't move
1540 newx = newx + width() - w;
1541 newy = ( newy + height() / 2 ) - ( h / 2 );
1542 break;
1543 case SouthWestGravity: // bottom left corner doesn't move
1544 newy = newy + height() - h;
1545 break;
1546 case SouthGravity: // middle of bottom border doesn't move
1547 newx = ( newx + width() / 2 ) - ( w / 2 );
1548 newy = newy + height() - h;
1549 break;
1550 case SouthEastGravity: // bottom right corner doesn't move
1551 newx = newx + width() - w;
1552 newy = newy + height() - h;
1553 break;
1555 // if it would be moved outside of workarea, keep it inside,
1556 // see also Client::computeWorkareaDiff()
1557 if( workarea_diff_x != INT_MIN && w <= area.width()) // was inside and can still fit
1559 if( newx < area.left())
1560 newx = area.left();
1561 if( newx + w > area.right() + 1 )
1562 newx = area.right() + 1 - w;
1563 assert( newx >= area.left() && newx + w <= area.right() + 1 ); // width was checked above
1565 if( workarea_diff_y != INT_MIN && h <= area.height()) // was inside and can still fit
1567 if( newy < area.top())
1568 newy = area.top();
1569 if( newy + h > area.bottom() + 1 )
1570 newy = area.bottom() + 1 - h;
1571 assert( newy >= area.top() && newy + h <= area.bottom() + 1 ); // height was checked above
1573 setGeometry( newx, newy, w, h, force );
1576 // _NET_MOVERESIZE_WINDOW
1577 void Client::NETMoveResizeWindow( int flags, int x, int y, int width, int height )
1579 int gravity = flags & 0xff;
1580 int value_mask = 0;
1581 if( flags & ( 1 << 8 ))
1582 value_mask |= CWX;
1583 if( flags & ( 1 << 9 ))
1584 value_mask |= CWY;
1585 if( flags & ( 1 << 10 ))
1586 value_mask |= CWWidth;
1587 if( flags & ( 1 << 11 ))
1588 value_mask |= CWHeight;
1589 configureRequest( value_mask, x, y, width, height, gravity, true );
1593 Returns whether the window is moveable or has a fixed
1594 position.
1596 bool Client::isMovable() const
1598 if( !motif_may_move || isFullScreen())
1599 return false;
1600 if( isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :)
1601 return false;
1602 if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
1603 return false;
1604 if( rules()->checkPosition( invalidPoint ) != invalidPoint ) // forced position
1605 return false;
1606 return true;
1610 Returns whether the window is resizable or has a fixed size.
1612 bool Client::isResizable() const
1614 if( !motif_may_resize || isFullScreen())
1615 return false;
1616 if( isSpecialWindow() || isSplash() || isToolbar())
1617 return false;
1618 if( maximizeMode() == MaximizeFull && !options->moveResizeMaximizedWindows() )
1619 return false;
1620 if( rules()->checkSize( QSize()).isValid()) // forced size
1621 return false;
1623 QSize min = minSize();
1624 QSize max = maxSize();
1625 return min.width() < max.width() || min.height() < max.height();
1629 Returns whether the window is maximizable or not
1631 bool Client::isMaximizable() const
1633 { // isMovable() and isResizable() may be false for maximized windows
1634 // with moving/resizing maximized windows disabled
1635 TemporaryAssign< MaximizeMode > tmp( max_mode, MaximizeRestore );
1636 if( !isMovable() || !isResizable() || isToolbar()) // SELI isToolbar() ?
1637 return false;
1639 if ( maximizeMode() != MaximizeRestore )
1640 return true;
1641 QSize max = maxSize();
1642 #if 0
1643 if( max.width() < 32767 || max.height() < 32767 ) // sizes are 16bit with X
1644 return false;
1645 #else
1646 // apparently there are enough apps which specify some arbitrary value
1647 // for their maximum size just for the fun of it
1648 QSize areasize = workspace()->clientArea( MaximizeArea, this ).size();
1649 if( max.width() < areasize.width() || max.height() < areasize.height())
1650 return false;
1651 #endif
1652 return true;
1657 Reimplemented to inform the client about the new window position.
1659 void Client::setGeometry( int x, int y, int w, int h, ForceGeometry_t force )
1661 // this code is also duplicated in Client::plainResize()
1662 // Ok, the shading geometry stuff. Generally, code doesn't care about shaded geometry,
1663 // simply because there are too many places dealing with geometry. Those places
1664 // ignore shaded state and use normal geometry, which they usually should get
1665 // from adjustedSize(). Such geometry comes here, and if the window is shaded,
1666 // the geometry is used only for client_size, since that one is not used when
1667 // shading. Then the frame geometry is adjusted for the shaded geometry.
1668 // This gets more complicated in the case the code does only something like
1669 // setGeometry( geometry()) - geometry() will return the shaded frame geometry.
1670 // Such code is wrong and should be changed to handle the case when the window is shaded,
1671 // for example using Client::clientSize().
1672 if( shade_geometry_change )
1673 ; // nothing
1674 else if( isShade())
1676 if( h == border_top + border_bottom )
1678 kDebug() << "Shaded geometry passed for size:";
1679 kDebug() << kBacktrace();
1681 else
1683 client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
1684 h = border_top + border_bottom;
1687 else
1689 client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
1691 QRect g( x, y, w, h );
1692 if( block_geometry_updates == 0 && g != rules()->checkGeometry( g ))
1694 kDebug() << "forced geometry fail:" << g << ":" << rules()->checkGeometry( g );
1695 kDebug() << kBacktrace();
1697 if( force == NormalGeometrySet && geom == g && pending_geometry_update == PendingGeometryNone )
1698 return;
1699 geom = g;
1700 if( block_geometry_updates != 0 )
1702 if( pending_geometry_update == PendingGeometryForced )
1703 {} // maximum, nothing needed
1704 else if( force == ForceGeometrySet )
1705 pending_geometry_update = PendingGeometryForced;
1706 else
1707 pending_geometry_update = PendingGeometryNormal;
1708 return;
1710 bool resized = ( geom_before_block.size() != geom.size() || pending_geometry_update == PendingGeometryForced );
1711 if( resized )
1713 resizeDecoration( QSize( w, h ));
1714 XMoveResizeWindow( display(), frameId(), x, y, w, h );
1715 if( !isShade())
1717 QSize cs = clientSize();
1718 XMoveResizeWindow( display(), wrapperId(), clientPos().x(), clientPos().y(),
1719 cs.width(), cs.height());
1720 XMoveResizeWindow( display(), window(), 0, 0, cs.width(), cs.height());
1722 updateShape();
1724 else
1725 XMoveWindow( display(), frameId(), x, y );
1726 // SELI TODO won't this be too expensive?
1727 updateWorkareaDiffs();
1728 sendSyntheticConfigureNotify();
1729 updateWindowRules();
1730 checkMaximizeGeometry();
1731 workspace()->checkActiveScreen( this );
1732 if( resized )
1734 discardWindowPixmap();
1735 if( scene != NULL )
1736 scene->windowGeometryShapeChanged( this );
1737 if( effects != NULL )
1738 static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), geom_before_block );
1740 addWorkspaceRepaint( geom_before_block );
1741 addWorkspaceRepaint( geom );
1742 geom_before_block = geom;
1745 void Client::plainResize( int w, int h, ForceGeometry_t force )
1747 // this code is also duplicated in Client::setGeometry(), and it's also commented there
1748 if( shade_geometry_change )
1749 ; // nothing
1750 else if( isShade())
1752 if( h == border_top + border_bottom )
1754 kDebug() << "Shaded geometry passed for size:";
1755 kDebug() << kBacktrace();
1757 else
1759 client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
1760 h = border_top + border_bottom;
1763 else
1765 client_size = QSize( w - border_left - border_right, h - border_top - border_bottom );
1767 QSize s( w, h );
1768 if( block_geometry_updates == 0 && s != rules()->checkSize( s ))
1770 kDebug() << "forced size fail:" << s << ":" << rules()->checkSize( s );
1771 kDebug() << kBacktrace();
1773 // resuming geometry updates is handled only in setGeometry()
1774 assert( pending_geometry_update == PendingGeometryNone || block_geometry_updates > 0 );
1775 if( force == NormalGeometrySet && geom.size() == s )
1776 return;
1777 geom.setSize( s );
1778 if( block_geometry_updates != 0 )
1780 if( pending_geometry_update == PendingGeometryForced )
1781 {} // maximum, nothing needed
1782 else if( force == ForceGeometrySet )
1783 pending_geometry_update = PendingGeometryForced;
1784 else
1785 pending_geometry_update = PendingGeometryNormal;
1786 return;
1788 resizeDecoration( s );
1789 XResizeWindow( display(), frameId(), w, h );
1790 // resizeDecoration( s );
1791 if( !isShade())
1793 QSize cs = clientSize();
1794 XMoveResizeWindow( display(), wrapperId(), clientPos().x(), clientPos().y(),
1795 cs.width(), cs.height());
1796 XMoveResizeWindow( display(), window(), 0, 0, cs.width(), cs.height());
1798 updateShape();
1799 updateWorkareaDiffs();
1800 sendSyntheticConfigureNotify();
1801 updateWindowRules();
1802 checkMaximizeGeometry();
1803 workspace()->checkActiveScreen( this );
1804 discardWindowPixmap();
1805 if( scene != NULL )
1806 scene->windowGeometryShapeChanged( this );
1807 if( effects != NULL )
1808 static_cast<EffectsHandlerImpl*>(effects)->windowGeometryShapeChanged( effectWindow(), geom_before_block );
1809 addWorkspaceRepaint( geom_before_block );
1810 addWorkspaceRepaint( geom );
1811 geom_before_block = geom;
1815 Reimplemented to inform the client about the new window position.
1817 void Client::move( int x, int y, ForceGeometry_t force )
1819 // resuming geometry updates is handled only in setGeometry()
1820 assert( pending_geometry_update == PendingGeometryNone || block_geometry_updates > 0 );
1821 QPoint p( x, y );
1822 if( block_geometry_updates == 0 && p != rules()->checkPosition( p ))
1824 kDebug() << "forced position fail:" << p << ":" << rules()->checkPosition( p );
1825 kDebug() << kBacktrace();
1827 if( force == NormalGeometrySet && geom.topLeft() == p )
1828 return;
1829 geom.moveTopLeft( p );
1830 if( block_geometry_updates != 0 )
1832 if( pending_geometry_update == PendingGeometryForced )
1833 {} // maximum, nothing needed
1834 else if( force == ForceGeometrySet )
1835 pending_geometry_update = PendingGeometryForced;
1836 else
1837 pending_geometry_update = PendingGeometryNormal;
1838 return;
1840 XMoveWindow( display(), frameId(), x, y );
1841 updateWorkareaDiffs();
1842 sendSyntheticConfigureNotify();
1843 updateWindowRules();
1844 checkMaximizeGeometry();
1845 workspace()->checkActiveScreen( this );
1846 // client itself is not damaged
1847 addWorkspaceRepaint( geom_before_block );
1848 addWorkspaceRepaint( geom ); // trigger repaint of window's new location
1849 geom_before_block = geom;
1852 void Client::blockGeometryUpdates( bool block )
1854 if( block )
1856 if( block_geometry_updates == 0 )
1857 pending_geometry_update = PendingGeometryNone;
1858 ++block_geometry_updates;
1860 else
1862 if( --block_geometry_updates == 0 )
1864 if( pending_geometry_update != PendingGeometryNone )
1866 if( isShade())
1867 setGeometry( QRect( pos(), adjustedSize()), NormalGeometrySet );
1868 else
1869 setGeometry( geometry(), NormalGeometrySet );
1870 pending_geometry_update = PendingGeometryNone;
1876 void Client::maximize( MaximizeMode m )
1878 setMaximize( m & MaximizeVertical, m & MaximizeHorizontal );
1882 Sets the maximization according to \a vertically and \a horizontally
1884 void Client::setMaximize( bool vertically, bool horizontally )
1885 { // changeMaximize() flips the state, so change from set->flip
1886 changeMaximize(
1887 max_mode & MaximizeVertical ? !vertically : vertically,
1888 max_mode & MaximizeHorizontal ? !horizontally : horizontally,
1889 false );
1892 void Client::changeMaximize( bool vertical, bool horizontal, bool adjust )
1894 if( !isMaximizable())
1895 return;
1897 MaximizeMode old_mode = max_mode;
1898 // 'adjust == true' means to update the size only, e.g. after changing workspace size
1899 if( !adjust )
1901 if( vertical )
1902 max_mode = MaximizeMode( max_mode ^ MaximizeVertical );
1903 if( horizontal )
1904 max_mode = MaximizeMode( max_mode ^ MaximizeHorizontal );
1907 max_mode = rules()->checkMaximize( max_mode );
1908 if( !adjust && max_mode == old_mode )
1909 return;
1911 GeometryUpdatesBlocker blocker( this );
1913 // maximing one way and unmaximizing the other way shouldn't happen,
1914 // so restore first and then maximize the other way
1915 if( ( old_mode == MaximizeVertical && max_mode == MaximizeHorizontal )
1916 || ( old_mode == MaximizeHorizontal && max_mode == MaximizeVertical ))
1918 changeMaximize( false, false, false ); // restore
1922 QRect clientArea = workspace()->clientArea( MaximizeArea, this );
1924 // save sizes for restoring, if maximalizing
1925 if( !adjust && !( y() == clientArea.top() && height() == clientArea.height()))
1927 geom_restore.setTop( y());
1928 geom_restore.setHeight( height());
1930 if( !adjust && !( x() == clientArea.left() && width() == clientArea.width()))
1932 geom_restore.setLeft( x());
1933 geom_restore.setWidth( width());
1936 if( !adjust )
1938 if(( vertical && !(old_mode & MaximizeVertical ))
1939 || ( horizontal && !( old_mode & MaximizeHorizontal )))
1940 Notify::raise( Notify::Maximize );
1941 else
1942 Notify::raise( Notify::UnMaximize );
1945 if( decoration != NULL ) // decorations may turn off some borders when maximized
1946 decoration->borders( border_left, border_right, border_top, border_bottom );
1948 // restore partial maximizations
1949 if ( old_mode==MaximizeFull && max_mode==MaximizeRestore )
1951 if ( maximizeModeRestore()==MaximizeVertical )
1953 max_mode = MaximizeVertical;
1954 maxmode_restore = MaximizeRestore;
1956 if ( maximizeModeRestore()==MaximizeHorizontal )
1958 max_mode = MaximizeHorizontal;
1959 maxmode_restore = MaximizeRestore;
1963 switch (max_mode)
1966 case MaximizeVertical:
1968 if( old_mode & MaximizeHorizontal ) // actually restoring from MaximizeFull
1970 if( geom_restore.width() == 0 )
1971 { // needs placement
1972 plainResize( adjustedSize( QSize( width() * 2 / 3, clientArea.height()), SizemodeFixedH ));
1973 workspace()->placeSmart( this, clientArea );
1975 else
1976 setGeometry( QRect(QPoint( geom_restore.x(), clientArea.top()),
1977 adjustedSize(QSize( geom_restore.width(), clientArea.height()), SizemodeFixedH )));
1979 else
1980 setGeometry( QRect(QPoint(x(), clientArea.top()),
1981 adjustedSize(QSize(width(), clientArea.height()), SizemodeFixedH )));
1982 info->setState( NET::MaxVert, NET::Max );
1983 break;
1986 case MaximizeHorizontal:
1988 if( old_mode & MaximizeVertical ) // actually restoring from MaximizeFull
1990 if( geom_restore.height() == 0 )
1991 { // needs placement
1992 plainResize( adjustedSize( QSize( clientArea.width(), height() * 2 / 3 ), SizemodeFixedW ));
1993 workspace()->placeSmart( this, clientArea );
1995 else
1996 setGeometry( QRect( QPoint(clientArea.left(), geom_restore.y()),
1997 adjustedSize(QSize(clientArea.width(), geom_restore.height()), SizemodeFixedW )));
1999 else
2000 setGeometry( QRect( QPoint(clientArea.left(), y()),
2001 adjustedSize(QSize(clientArea.width(), height()), SizemodeFixedW )));
2002 info->setState( NET::MaxHoriz, NET::Max );
2003 break;
2006 case MaximizeRestore:
2008 QRect restore = geometry();
2009 // when only partially maximized, geom_restore may not have the other dimension remembered
2010 if( old_mode & MaximizeVertical )
2012 restore.setTop( geom_restore.top());
2013 restore.setBottom( geom_restore.bottom());
2015 if( old_mode & MaximizeHorizontal )
2017 restore.setLeft( geom_restore.left());
2018 restore.setRight( geom_restore.right());
2020 if( !restore.isValid())
2022 QSize s = QSize( clientArea.width()*2/3, clientArea.height()*2/3 );
2023 if( geom_restore.width() > 0 )
2024 s.setWidth( geom_restore.width());
2025 if( geom_restore.height() > 0 )
2026 s.setHeight( geom_restore.height());
2027 plainResize( adjustedSize( s ));
2028 workspace()->placeSmart( this, clientArea );
2029 restore = geometry();
2030 if( geom_restore.width() > 0 )
2031 restore.moveLeft( geom_restore.x());
2032 if( geom_restore.height() > 0 )
2033 restore.moveTop( geom_restore.y());
2035 setGeometry( restore );
2036 info->setState( 0, NET::Max );
2037 break;
2040 case MaximizeFull:
2042 if( !adjust )
2044 if( old_mode & MaximizeVertical )
2045 maxmode_restore = MaximizeVertical;
2046 if( old_mode & MaximizeHorizontal )
2047 maxmode_restore = MaximizeHorizontal;
2049 QSize adjSize = adjustedSize(clientArea.size(), SizemodeMax );
2050 QRect r = QRect(clientArea.topLeft(), adjSize);
2051 setGeometry( r );
2052 info->setState( NET::Max, NET::Max );
2053 break;
2055 default:
2056 break;
2059 updateAllowedActions();
2060 if( decoration != NULL )
2061 decoration->maximizeChange();
2062 updateWindowRules();
2065 void Client::resetMaximize()
2067 if( max_mode == MaximizeRestore )
2068 return;
2069 max_mode = MaximizeRestore;
2070 Notify::raise( Notify::UnMaximize );
2071 info->setState( 0, NET::Max );
2072 updateAllowedActions();
2073 if( decoration != NULL )
2074 decoration->borders( border_left, border_right, border_top, border_bottom );
2075 if( isShade())
2076 setGeometry( QRect( pos(), sizeForClientSize( clientSize())), ForceGeometrySet );
2077 else
2078 setGeometry( geometry(), ForceGeometrySet );
2079 if( decoration != NULL )
2080 decoration->maximizeChange();
2083 void Client::checkMaximizeGeometry()
2085 // when adding new bail-out conditions here, checkMaximizeGeometry() needs to be called
2086 // when after the condition is no longer true
2087 if( isShade())
2088 return;
2089 if( isMove() || isResize()) // this is because of the option to disallow moving/resizing of max-ed windows
2090 return;
2091 // Just in case.
2092 static int recursion_protection = 0;
2093 if( recursion_protection > 3 )
2095 kWarning( 1212 ) << "Check maximize overflow - you loose!" ;
2096 kWarning( 1212 ) << kBacktrace() ;
2097 return;
2099 ++recursion_protection;
2100 QRect max_area = workspace()->clientArea( MaximizeArea, this );
2101 if( geometry() == max_area )
2103 if( max_mode != MaximizeFull )
2104 maximize( MaximizeFull );
2106 else if( x() == max_area.left() && width() == max_area.width())
2108 if( max_mode != MaximizeHorizontal )
2109 maximize( MaximizeHorizontal );
2111 else if( y() == max_area.top() && height() == max_area.height())
2113 if( max_mode != MaximizeVertical )
2114 maximize( MaximizeVertical );
2116 else if( max_mode != MaximizeRestore )
2118 resetMaximize(); // not maximize( MaximizeRestore ), that'd change geometry - this is called from setGeometry()
2120 --recursion_protection;
2123 bool Client::isFullScreenable( bool fullscreen_hack ) const
2125 if( !rules()->checkFullScreen( true ))
2126 return false;
2127 if( fullscreen_hack )
2128 return isNormalWindow();
2129 if( rules()->checkStrictGeometry( false ))
2131 // the app wouldn't fit exactly fullscreen geometry due its strict geometry requirements
2132 QRect fsarea = workspace()->clientArea( FullScreenArea, this );
2133 if( sizeForClientSize( fsarea.size(), SizemodeAny, true ) != fsarea.size())
2134 return false;
2136 // don't check size constrains - some apps request fullscreen despite requesting fixed size
2137 return !isSpecialWindow(); // also better disallow only weird types to go fullscreen
2140 bool Client::userCanSetFullScreen() const
2142 if( fullscreen_mode == FullScreenHack )
2143 return false;
2144 if( !isFullScreenable( false ))
2145 return false;
2146 // isMaximizable() returns false if fullscreen
2147 TemporaryAssign< FullScreenMode > tmp( fullscreen_mode, FullScreenNone );
2148 return isNormalWindow() && isMaximizable();
2151 void Client::setFullScreen( bool set, bool user )
2153 if( !isFullScreen() && !set )
2154 return;
2155 if( fullscreen_mode == FullScreenHack )
2156 return;
2157 if( user && !userCanSetFullScreen())
2158 return;
2159 set = rules()->checkFullScreen( set );
2160 setShade( ShadeNone );
2161 bool was_fs = isFullScreen();
2162 if( !was_fs )
2163 geom_fs_restore = geometry();
2164 fullscreen_mode = set ? FullScreenNormal : FullScreenNone;
2165 if( was_fs == isFullScreen())
2166 return;
2167 StackingUpdatesBlocker blocker1( workspace());
2168 GeometryUpdatesBlocker blocker2( this );
2169 workspace()->updateClientLayer( this ); // active fullscreens get different layer
2170 info->setState( isFullScreen() ? NET::FullScreen : 0, NET::FullScreen );
2171 updateDecoration( false, false );
2172 if( isFullScreen())
2173 setGeometry( workspace()->clientArea( FullScreenArea, this ));
2174 else
2176 if( !geom_fs_restore.isNull())
2177 setGeometry( QRect( geom_fs_restore.topLeft(), adjustedSize( geom_fs_restore.size())));
2178 // TODO isShaded() ?
2179 else
2180 { // does this ever happen?
2181 setGeometry( workspace()->clientArea( MaximizeArea, this ));
2184 updateWindowRules();
2187 int Client::checkFullScreenHack( const QRect& geom ) const
2189 // if it's noborder window, and has size of one screen or the whole desktop geometry, it's fullscreen hack
2190 if( noBorder() && !isUserNoBorder() && isFullScreenable( true ))
2192 if( geom.size() == workspace()->clientArea( FullArea, geom.center(), desktop()).size())
2193 return 2; // full area fullscreen hack
2194 if( geom.size() == workspace()->clientArea( ScreenArea, geom.center(), desktop()).size())
2195 return 1; // xinerama-aware fullscreen hack
2197 return 0;
2200 void Client::updateFullScreenHack( const QRect& geom )
2202 int type = checkFullScreenHack( geom );
2203 if( fullscreen_mode == FullScreenNone && type != 0 )
2205 fullscreen_mode = FullScreenHack;
2206 updateDecoration( false, false );
2207 QRect geom;
2208 if( rules()->checkStrictGeometry( false ))
2210 geom = type == 2 // 1 - it's xinerama-aware fullscreen hack, 2 - it's full area
2211 ? workspace()->clientArea( FullArea, geom.center(), desktop())
2212 : workspace()->clientArea( ScreenArea, geom.center(), desktop());
2214 else
2215 geom = workspace()->clientArea( FullScreenArea, geom.center(), desktop());
2216 setGeometry( geom );
2218 else if( fullscreen_mode == FullScreenHack && type == 0 )
2220 fullscreen_mode = FullScreenNone;
2221 updateDecoration( false, false );
2222 // whoever called this must setup correct geometry
2224 StackingUpdatesBlocker blocker( workspace());
2225 workspace()->updateClientLayer( this ); // active fullscreens get different layer
2228 static QRect* visible_bound = 0;
2229 static GeometryTip* geometryTip = 0;
2231 void Client::drawbound( const QRect& geom )
2233 assert( visible_bound == NULL );
2234 visible_bound = new QRect( geom );
2235 doDrawbound( *visible_bound, false );
2238 void Client::clearbound()
2240 if( visible_bound == NULL )
2241 return;
2242 doDrawbound( *visible_bound, true );
2243 delete visible_bound;
2244 visible_bound = 0;
2247 void Client::doDrawbound( const QRect& geom, bool clear )
2249 if( decoration != NULL && decoration->drawbound( geom, clear ))
2250 return; // done by decoration
2251 XGCValues xgc;
2252 xgc.function = GXxor;
2253 xgc.foreground = WhitePixel( display(), DefaultScreen( display()));
2254 xgc.line_width = 5;
2255 xgc.subwindow_mode = IncludeInferiors;
2256 GC gc = XCreateGC( display(), DefaultRootWindow( display()),
2257 GCFunction | GCForeground | GCLineWidth | GCSubwindowMode, &xgc );
2258 // the line is 5 pixel thick, so compensate for the extra two pixels
2259 // on outside (#88657)
2260 QRect g = geom;
2261 if( g.width() > 5 )
2263 g.setLeft( g.left() + 2 );
2264 g.setRight( g.right() - 2 );
2266 if( g.height() > 5 )
2268 g.setTop( g.top() + 2 );
2269 g.setBottom( g.bottom() - 2 );
2271 XDrawRectangle( display(), DefaultRootWindow( display()), gc, g.x(), g.y(), g.width(), g.height());
2272 XFreeGC( display(), gc );
2275 void Client::positionGeometryTip()
2277 assert( isMove() || isResize());
2278 // Position and Size display
2279 if (options->showGeometryTip())
2281 if( !geometryTip )
2282 { // save under is not necessary with opaque, and seem to make things slower
2283 bool save_under = ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
2284 || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque );
2285 geometryTip = new GeometryTip( &xSizeHint, save_under );
2287 QRect wgeom( moveResizeGeom ); // position of the frame, size of the window itself
2288 wgeom.setWidth( wgeom.width() - ( width() - clientSize().width()));
2289 wgeom.setHeight( wgeom.height() - ( height() - clientSize().height()));
2290 if( isShade())
2291 wgeom.setHeight( 0 );
2292 geometryTip->setGeometry( wgeom );
2293 if( !geometryTip->isVisible())
2295 geometryTip->show();
2296 geometryTip->raise();
2301 class EatAllPaintEvents
2302 : public QObject
2304 protected:
2305 virtual bool eventFilter( QObject* o, QEvent* e )
2306 { return e->type() == QEvent::Paint && o != geometryTip; }
2309 static EatAllPaintEvents* eater = 0;
2311 bool Client::startMoveResize()
2313 assert( !moveResizeMode );
2314 assert( QWidget::keyboardGrabber() == NULL );
2315 assert( QWidget::mouseGrabber() == NULL );
2316 stopDelayedMoveResize();
2317 if( QApplication::activePopupWidget() != NULL )
2318 return false; // popups have grab
2319 bool has_grab = false;
2320 // This reportedly improves smoothness of the moveresize operation,
2321 // something with Enter/LeaveNotify events, looks like XFree performance problem or something *shrug*
2322 // (http://lists.kde.org/?t=107302193400001&r=1&w=2)
2323 XSetWindowAttributes attrs;
2324 QRect r = workspace()->clientArea( FullArea, this );
2325 move_resize_grab_window = XCreateWindow( display(), rootWindow(), r.x(), r.y(),
2326 r.width(), r.height(), 0, CopyFromParent, InputOnly, CopyFromParent, 0, &attrs );
2327 XMapRaised( display(), move_resize_grab_window );
2328 if( XGrabPointer( display(), move_resize_grab_window, False,
2329 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask,
2330 GrabModeAsync, GrabModeAsync, move_resize_grab_window, cursor.handle(), xTime() ) == Success )
2331 has_grab = true;
2332 if( grabXKeyboard( frameId()))
2333 has_grab = move_resize_has_keyboard_grab = true;
2334 if( !has_grab ) // at least one grab is necessary in order to be able to finish move/resize
2336 XDestroyWindow( display(), move_resize_grab_window );
2337 move_resize_grab_window = None;
2338 return false;
2340 if ( maximizeMode() != MaximizeRestore )
2341 resetMaximize();
2342 moveResizeMode = true;
2343 workspace()->setClientIsMoving(this);
2344 initialMoveResizeGeom = moveResizeGeom = geometry();
2345 checkUnrestrictedMoveResize();
2346 if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
2347 || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
2349 grabXServer();
2350 kapp->sendPostedEvents();
2351 // we have server grab -> nothing should cause paint events
2352 // unfortunately, that's not completely true, Qt may generate
2353 // paint events on some widgets due to FocusIn(?)
2354 // eat them, otherwise XOR painting will be broken (#58054)
2355 // paint events for the geometrytip need to be allowed, though
2356 eater = new EatAllPaintEvents;
2357 // not needed anymore? kapp->installEventFilter( eater );
2359 Notify::raise( isResize() ? Notify::ResizeStart : Notify::MoveStart );
2360 if( effects )
2361 static_cast<EffectsHandlerImpl*>(effects)->windowUserMovedResized( effectWindow(), true, false );
2362 if( options->electricBorders() == Options::ElectricMoveOnly )
2363 workspace()->reserveElectricBorderSwitching( true );
2364 return true;
2367 void Client::finishMoveResize( bool cancel )
2369 leaveMoveResize();
2370 if( cancel )
2371 setGeometry( initialMoveResizeGeom );
2372 else
2373 setGeometry( moveResizeGeom );
2374 checkMaximizeGeometry();
2375 // FRAME update();
2376 Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd );
2377 if( effects )
2378 static_cast<EffectsHandlerImpl*>(effects)->windowUserMovedResized( effectWindow(), false, true );
2381 void Client::leaveMoveResize()
2383 clearbound();
2384 if (geometryTip)
2386 geometryTip->hide();
2387 delete geometryTip;
2388 geometryTip = NULL;
2390 if ( ( isMove() && rules()->checkMoveResizeMode( options->moveMode ) != Options::Opaque )
2391 || ( isResize() && rules()->checkMoveResizeMode( options->resizeMode ) != Options::Opaque ) )
2392 ungrabXServer();
2393 if( move_resize_has_keyboard_grab )
2394 ungrabXKeyboard();
2395 move_resize_has_keyboard_grab = false;
2396 XUngrabPointer( display(), xTime() );
2397 XDestroyWindow( display(), move_resize_grab_window );
2398 move_resize_grab_window = None;
2399 workspace()->setClientIsMoving(0);
2400 if( move_faked_activity )
2401 workspace()->unfakeActivity( this );
2402 move_faked_activity = false;
2403 moveResizeMode = false;
2404 delete eater;
2405 eater = 0;
2406 delete sync_timeout;
2407 sync_timeout = NULL;
2408 if( options->electricBorders() == Options::ElectricMoveOnly )
2409 workspace()->reserveElectricBorderSwitching( false );
2412 // This function checks if it actually makes sense to perform a restricted move/resize.
2413 // If e.g. the titlebar is already outside of the workarea, there's no point in performing
2414 // a restricted move resize, because then e.g. resize would also move the window (#74555).
2415 // NOTE: Most of it is duplicated from handleMoveResize().
2416 void Client::checkUnrestrictedMoveResize()
2418 if( unrestrictedMoveResize )
2419 return;
2420 QRect desktopArea = workspace()->clientArea( WorkArea, moveResizeGeom.center(), desktop());
2421 int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
2422 // restricted move/resize - keep at least part of the titlebar always visible
2423 // how much must remain visible when moved away in that direction
2424 left_marge = qMin( 100 + border_right, moveResizeGeom.width());
2425 right_marge = qMin( 100 + border_left, moveResizeGeom.width());
2426 // width/height change with opaque resizing, use the initial ones
2427 titlebar_marge = initialMoveResizeGeom.height();
2428 top_marge = border_bottom;
2429 bottom_marge = border_top;
2430 if( isResize())
2432 if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
2433 unrestrictedMoveResize = true;
2434 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2435 unrestrictedMoveResize = true;
2436 if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2437 unrestrictedMoveResize = true;
2438 if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2439 unrestrictedMoveResize = true;
2440 if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
2441 unrestrictedMoveResize = true;
2443 if( isMove())
2445 if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
2446 unrestrictedMoveResize = true;
2447 // no need to check top_marge, titlebar_marge already handles it
2448 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2449 unrestrictedMoveResize = true;
2450 if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2451 unrestrictedMoveResize = true;
2452 if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2453 unrestrictedMoveResize = true;
2457 // When the user pressed mouse on the titlebar, don't activate move immediatelly,
2458 // since it may be just a click. Activate instead after a delay. Move used to be
2459 // activated only after moving by several pixels, but that looks bad.
2460 void Client::startDelayedMoveResize()
2462 delete delayedMoveResizeTimer;
2463 delayedMoveResizeTimer = new QTimer( this );
2464 connect( delayedMoveResizeTimer, SIGNAL( timeout()), this, SLOT( delayedMoveResize()));
2465 delayedMoveResizeTimer->setSingleShot( true );
2466 delayedMoveResizeTimer->start( QApplication::doubleClickInterval());
2469 void Client::stopDelayedMoveResize()
2471 delete delayedMoveResizeTimer;
2472 delayedMoveResizeTimer = NULL;
2475 void Client::delayedMoveResize()
2477 assert( buttonDown );
2478 if( !startMoveResize())
2479 buttonDown = false;
2480 updateCursor();
2481 stopDelayedMoveResize();
2484 void Client::handleMoveResize( int x, int y, int x_root, int y_root )
2486 if(( mode == PositionCenter && !isMovable())
2487 || ( mode != PositionCenter && ( isShade() || !isResizable())))
2488 return;
2490 if ( !moveResizeMode )
2492 QPoint p( QPoint( x, y ) - moveOffset );
2493 if (p.manhattanLength() >= 6)
2495 if( !startMoveResize())
2497 buttonDown = false;
2498 updateCursor();
2499 return;
2501 updateCursor();
2503 else
2504 return;
2507 // ShadeHover or ShadeActive, ShadeNormal was already avoided above
2508 if ( mode != PositionCenter && shade_mode != ShadeNone )
2509 setShade( ShadeNone );
2511 QPoint globalPos( x_root, y_root );
2512 // these two points limit the geometry rectangle, i.e. if bottomleft resizing is done,
2513 // the bottomleft corner should be at is at (topleft.x(), bottomright().y())
2514 QPoint topleft = globalPos - moveOffset;
2515 QPoint bottomright = globalPos + invertedMoveOffset;
2516 QRect previousMoveResizeGeom = moveResizeGeom;
2518 // TODO move whole group when moving its leader or when the leader is not mapped?
2520 // compute bounds
2521 // NOTE: This is duped in checkUnrestrictedMoveResize().
2522 QRect desktopArea = workspace()->clientArea( WorkArea, globalPos, desktop());
2523 int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
2524 if( unrestrictedMoveResize ) // unrestricted, just don't let it go out completely
2525 left_marge = right_marge = top_marge = bottom_marge = titlebar_marge = 5;
2526 else // restricted move/resize - keep at least part of the titlebar always visible
2528 // how much must remain visible when moved away in that direction
2529 left_marge = qMin( 100 + border_right, moveResizeGeom.width());
2530 right_marge = qMin( 100 + border_left, moveResizeGeom.width());
2531 // width/height change with opaque resizing, use the initial ones
2532 titlebar_marge = initialMoveResizeGeom.height();
2533 top_marge = border_bottom;
2534 bottom_marge = border_top;
2537 bool update = false;
2538 if( isResize())
2540 // first resize (without checking constrains), then snap, then check bounds, then check constrains
2541 QRect orig = initialMoveResizeGeom;
2542 Sizemode sizemode = SizemodeAny;
2543 switch ( mode )
2545 case PositionTopLeft:
2546 moveResizeGeom = QRect( topleft, orig.bottomRight() ) ;
2547 break;
2548 case PositionBottomRight:
2549 moveResizeGeom = QRect( orig.topLeft(), bottomright ) ;
2550 break;
2551 case PositionBottomLeft:
2552 moveResizeGeom = QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ;
2553 break;
2554 case PositionTopRight:
2555 moveResizeGeom = QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
2556 break;
2557 case PositionTop:
2558 moveResizeGeom = QRect( QPoint( orig.left(), topleft.y() ), orig.bottomRight() ) ;
2559 sizemode = SizemodeFixedH; // try not to affect height
2560 break;
2561 case PositionBottom:
2562 moveResizeGeom = QRect( orig.topLeft(), QPoint( orig.right(), bottomright.y() ) ) ;
2563 sizemode = SizemodeFixedH;
2564 break;
2565 case PositionLeft:
2566 moveResizeGeom = QRect( QPoint( topleft.x(), orig.top() ), orig.bottomRight() ) ;
2567 sizemode = SizemodeFixedW;
2568 break;
2569 case PositionRight:
2570 moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), orig.bottom() ) ) ;
2571 sizemode = SizemodeFixedW;
2572 break;
2573 case PositionCenter:
2574 default:
2575 assert( false );
2576 break;
2579 // adjust new size to snap to other windows/borders
2580 moveResizeGeom = workspace()->adjustClientSize( this, moveResizeGeom, mode );
2582 // NOTE: This is duped in checkUnrestrictedMoveResize().
2583 if( moveResizeGeom.bottom() < desktopArea.top() + top_marge )
2584 moveResizeGeom.setBottom( desktopArea.top() + top_marge );
2585 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2586 moveResizeGeom.setTop( desktopArea.bottom() - bottom_marge );
2587 if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2588 moveResizeGeom.setRight( desktopArea.left() + left_marge );
2589 if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2590 moveResizeGeom.setLeft(desktopArea.right() - right_marge );
2591 if( !unrestrictedMoveResize && moveResizeGeom.top() < desktopArea.top() ) // titlebar mustn't go out
2592 moveResizeGeom.setTop( desktopArea.top());
2594 QSize size = adjustedSize( moveResizeGeom.size(), sizemode );
2595 // the new topleft and bottomright corners (after checking size constrains), if they'll be needed
2596 topleft = QPoint( moveResizeGeom.right() - size.width() + 1, moveResizeGeom.bottom() - size.height() + 1 );
2597 bottomright = QPoint( moveResizeGeom.left() + size.width() - 1, moveResizeGeom.top() + size.height() - 1 );
2598 orig = moveResizeGeom;
2599 switch ( mode )
2600 { // these 4 corners ones are copied from above
2601 case PositionTopLeft:
2602 moveResizeGeom = QRect( topleft, orig.bottomRight() ) ;
2603 break;
2604 case PositionBottomRight:
2605 moveResizeGeom = QRect( orig.topLeft(), bottomright ) ;
2606 break;
2607 case PositionBottomLeft:
2608 moveResizeGeom = QRect( QPoint( topleft.x(), orig.y() ), QPoint( orig.right(), bottomright.y()) ) ;
2609 break;
2610 case PositionTopRight:
2611 moveResizeGeom = QRect( QPoint( orig.x(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
2612 break;
2613 // The side ones can't be copied exactly - if aspect ratios are specified, both dimensions may change.
2614 // Therefore grow to the right/bottom if needed.
2615 // TODO it should probably obey gravity rather than always using right/bottom ?
2616 case PositionTop:
2617 moveResizeGeom = QRect( QPoint( orig.left(), topleft.y() ), QPoint( bottomright.x(), orig.bottom()) ) ;
2618 break;
2619 case PositionBottom:
2620 moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ;
2621 break;
2622 case PositionLeft:
2623 moveResizeGeom = QRect( QPoint( topleft.x(), orig.top() ), QPoint( orig.right(), bottomright.y()));
2624 break;
2625 case PositionRight:
2626 moveResizeGeom = QRect( orig.topLeft(), QPoint( bottomright.x(), bottomright.y() ) ) ;
2627 break;
2628 case PositionCenter:
2629 default:
2630 assert( false );
2631 break;
2633 if( moveResizeGeom.size() != previousMoveResizeGeom.size())
2634 update = true;
2636 else if( isMove())
2638 assert( mode == PositionCenter );
2639 // first move, then snap, then check bounds
2640 moveResizeGeom.moveTopLeft( topleft );
2641 moveResizeGeom.moveTopLeft( workspace()->adjustClientPosition( this, moveResizeGeom.topLeft() ) );
2642 // NOTE: This is duped in checkUnrestrictedMoveResize().
2643 if( moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge - 1 ) // titlebar mustn't go out
2644 moveResizeGeom.moveBottom( desktopArea.top() + titlebar_marge - 1 );
2645 // no need to check top_marge, titlebar_marge already handles it
2646 if( moveResizeGeom.top() > desktopArea.bottom() - bottom_marge )
2647 moveResizeGeom.moveTop( desktopArea.bottom() - bottom_marge );
2648 if( moveResizeGeom.right() < desktopArea.left() + left_marge )
2649 moveResizeGeom.moveRight( desktopArea.left() + left_marge );
2650 if( moveResizeGeom.left() > desktopArea.right() - right_marge )
2651 moveResizeGeom.moveLeft(desktopArea.right() - right_marge );
2652 if( moveResizeGeom.topLeft() != previousMoveResizeGeom.topLeft())
2653 update = true;
2655 else
2656 assert( false );
2658 if( isResize())
2660 if( sync_timeout != NULL )
2662 sync_resize_pending = true;
2663 return;
2667 if( update )
2668 performMoveResize();
2669 if ( isMove() )
2670 workspace()->checkElectricBorder(globalPos, xTime());
2673 void Client::performMoveResize()
2675 #ifdef HAVE_XSYNC
2676 if( isResize() && sync_counter != None )
2678 sync_timeout = new QTimer( this );
2679 connect( sync_timeout, SIGNAL( timeout()), SLOT( syncTimeout()));
2680 sync_timeout->setSingleShot( true );
2681 sync_timeout->start( 500 );
2682 sendSyncRequest();
2684 #endif
2685 sync_resize_pending = false;
2686 if( rules()->checkMoveResizeMode
2687 ( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque )
2689 setGeometry( moveResizeGeom );
2690 positionGeometryTip();
2692 else if( rules()->checkMoveResizeMode
2693 ( isResize() ? options->resizeMode : options->moveMode ) == Options::Transparent )
2695 clearbound(); // it's necessary to move the geometry tip when there's no outline
2696 positionGeometryTip(); // shown, otherwise it would cause repaint problems in case
2697 drawbound( moveResizeGeom ); // they overlap; the paint event will come after this,
2698 } // so the geometry tip will be painted above the outline
2699 if( effects )
2700 static_cast<EffectsHandlerImpl*>(effects)->windowUserMovedResized( effectWindow(), false, false );
2703 void Client::syncTimeout()
2705 sync_timeout->deleteLater();
2706 sync_timeout = NULL;
2707 if( sync_resize_pending )
2708 performMoveResize();
2711 } // namespace