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.
30 #include "workspace.h"
32 #include <kapplication.h>
35 #include <kwindowsystem.h>
37 #include "placement.h"
38 #include "notifications.h"
39 #include "geometrytip.h"
44 #include <kephal/screens.h>
49 //********************************************
51 //********************************************
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
);
65 destroyElectricBorders();
66 updateElectricBorders();
70 QTimer::singleShot( 0, this, SLOT( setupCompositing() ) );
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
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();
98 screens
[iS
] = Kephal::ScreenUtils::screenGeometry( iS
);
101 i
<= numberOfDesktops();
104 new_wareas
[ i
] = desktopArea
;
105 new_sareas
[ i
].resize( nscreens
);
109 new_sareas
[ i
][ iS
] = screens
[ iS
];
111 for ( ClientList::ConstIterator it
= clients
.constBegin(); it
!= clients
.constEnd(); ++it
)
113 if( !(*it
)->hasStrut())
115 QRect r
= (*it
)->adjustedClientArea( desktopArea
, desktopArea
);
116 if( (*it
)->isOnAllDesktops())
119 i
<= numberOfDesktops();
122 new_wareas
[ i
] = new_wareas
[ i
].intersected( r
);
127 new_sareas
[ i
][ iS
] = new_sareas
[ i
][ iS
].intersected(
128 (*it
)->adjustedClientArea( desktopArea
, screens
[ iS
] ));
134 new_wareas
[ (*it
)->desktop() ] = new_wareas
[ (*it
)->desktop() ].intersected( r
);
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
] ));
148 i
<= numberOfDesktops();
154 kDebug (1212) << "new_sarea: " << new_sareas
[ i
][ iS
];
157 // TODO topmenu update for screenarea changes?
158 if( topmenu_space
!= NULL
)
160 QRect topmenu_area
= desktopArea
;
161 topmenu_area
.setTop( topMenuHeight());
163 i
<= numberOfDesktops();
165 new_wareas
[ i
] = new_wareas
[ i
].intersected( topmenu_area
);
168 bool changed
= force
;
170 if(screenarea
.isEmpty())
174 !changed
&& i
<= numberOfDesktops();
177 if( workarea
[ i
] != new_wareas
[ i
] )
179 if( screenarea
[ i
].size() != new_sareas
[ i
].size())
182 !changed
&& iS
< nscreens
;
184 if (new_sareas
[ i
][ iS
] != screenarea
[ i
][ iS
])
190 workarea
= new_wareas
;
191 screenarea
= new_sareas
;
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();
206 (*it
)->checkWorkspacePosition();
207 for( ClientList::ConstIterator it
= desktops
.constBegin();
208 it
!= desktops
.constEnd();
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().
229 QRect
Workspace::clientArea( clientAreaOption opt
, int screen
, int desktop
) const
231 if( desktop
== NETWinInfo::OnAllDesktops
|| desktop
== 0 )
232 desktop
= currentDesktop();
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
];
246 if (options
->xineramaMaximizeEnabled
)
250 case MaximizeFullArea
:
251 if (options
->xineramaMaximizeEnabled
)
252 return Kephal::ScreenUtils::screenGeometry( screen
);
254 return Kephal::ScreenUtils::desktopGeometry();
256 if (options
->xineramaFullscreenEnabled
)
257 return Kephal::ScreenUtils::screenGeometry( screen
);
259 return Kephal::ScreenUtils::desktopGeometry();
261 if (options
->xineramaPlacementEnabled
)
266 if (options
->xineramaMovementEnabled
)
267 return Kephal::ScreenUtils::screenGeometry( screen
);
269 return Kephal::ScreenUtils::desktopGeometry();
273 return Kephal::ScreenUtils::desktopGeometry();
275 return Kephal::ScreenUtils::screenGeometry( screen
);
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());
316 const int ry(cy
+ch
); //these don't change
318 int nx(cx
), ny(cy
); //buffers
320 int deltaY(ymax
); //minimum distance to other clients
322 int lx
, ly
, lrx
, lry
; //coords and size for the comparison client, l
325 int snap
= options
->borderSnapZone
; //snap trigger
328 if ((sOWO
?(cx
<xmin
):true) && (qAbs(xmin
-cx
)<snap
))
333 if ((sOWO
?(rx
>xmax
):true) && (qAbs(rx
-xmax
)<snap
) && (qAbs(xmax
-rx
) < deltaX
))
339 if ((sOWO
?(cy
<ymin
):true) && (qAbs(ymin
-cy
)<snap
))
344 if ((sOWO
?(ry
>ymax
):true) && (qAbs(ry
-ymax
)<snap
) && (qAbs(ymax
-ry
) < deltaY
))
352 snap
= options
->windowSnapZone
;
355 QList
<Client
*>::ConstIterator l
;
356 for (l
= clients
.constBegin();l
!= clients
.constEnd();++l
)
358 if ((*l
)->isOnDesktop(currentDesktop()) &&
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
);
376 if ((sOWO
?(rx
>lx
):true) && (qAbs(rx
-lx
)<snap
) && ( qAbs( rx
- lx
)<deltaX
) )
378 deltaX
= qAbs(rx
- lx
);
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
);
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
);
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
);
408 if ((sOWO
?(cy
<ly
):true) && (qAbs(cy
-ly
)<snap
) && (qAbs(cy
-ly
) < deltaY
))
410 deltaY
= qAbs( cy
- 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
);
421 if ((sOWO
?(cx
<lx
):true) && (qAbs(cx
-lx
)<snap
) && (qAbs(cx
-lx
) < deltaX
))
423 deltaX
= qAbs( cx
- lx
);
432 snap
= options
->centerSnapZone
; //snap trigger
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
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
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
455 nx
= (xmin
+ xmax
)/2 - cw
/2;
460 pos
= QPoint(nx
, ny
);
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
);
488 int deltaY(ymax
); //minimum distance to other clients
490 int lx
, ly
, lrx
, lry
; //coords and size for the comparison client, l
493 int snap
= options
->borderSnapZone
; //snap trigger
499 #define SNAP_BORDER_TOP \
500 if ((sOWO?(newcy<ymin):true) && (qAbs(ymin-newcy)<deltaY)) \
502 deltaY = qAbs(ymin-newcy); \
506 #define SNAP_BORDER_BOTTOM \
507 if ((sOWO?(newry>ymax):true) && (qAbs(ymax-newry)<deltaY)) \
509 deltaY = qAbs(ymax-newcy); \
513 #define SNAP_BORDER_LEFT \
514 if ((sOWO?(newcx<xmin):true) && (qAbs(xmin-newcx)<deltaX)) \
516 deltaX = qAbs(xmin-newcx); \
520 #define SNAP_BORDER_RIGHT \
521 if ((sOWO?(newrx>xmax):true) && (qAbs(xmax-newrx)<deltaX)) \
523 deltaX = qAbs(xmax-newrx); \
528 case PositionBottomRight
:
538 case PositionTopLeft
:
548 case PositionTopRight
:
552 case PositionBottomLeft
:
565 snap
= options
->windowSnapZone
;
570 QList
<Client
*>::ConstIterator l
;
571 for (l
= clients
.constBegin();l
!= clients
.constEnd();++l
)
573 if ((*l
)->isOnDesktop(currentDesktop()) &&
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) \
592 && (qAbs( lry - newcy ) < deltaY) ) { \
593 deltaY = qAbs( lry - newcy ); \
597 #define SNAP_WINDOW_BOTTOM if ( (sOWO?(newry>ly):true) \
599 && (qAbs( ly - newry ) < deltaY) ) { \
600 deltaY = qAbs( ly - newry ); \
604 #define SNAP_WINDOW_LEFT if ( (sOWO?(newcx<lrx):true) \
606 && (qAbs( lrx - newcx ) < deltaX)) { \
607 deltaX = qAbs( lrx - newcx ); \
611 #define SNAP_WINDOW_RIGHT if ( (sOWO?(newrx>lx):true) \
613 && (qAbs( lx - newrx ) < deltaX)) \
615 deltaX = qAbs( lx - newrx ); \
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 ); \
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 ); \
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 ); \
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 ); \
649 case PositionBottomRight
:
663 case PositionTopLeft
:
677 case PositionTopRight
:
683 case PositionBottomLeft
:
698 //snap = options->centerSnapZone;
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
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.
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()) )
743 initPositioning
->placeCascaded(*it
, area
);
748 Unclutters the current desktop by smart-placing all clients
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()) )
760 initPositioning
->placeSmart(clients
.at( i
), QRect());
765 void Workspace::updateTopMenuGeometry( Client
* c
)
767 if( !managingTopMenus())
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();
788 // c == NULL - update all, including topmenu_space
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();
796 updateTopMenuGeometry( *it
);
799 //********************************************
801 //********************************************
804 void Client::keepInArea( QRect area
, bool 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()));
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() ))
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
845 // topmenu area is reserved in updateClientArea()
848 NETExtendedStrut str
= strut();
849 QRect stareaL
= QRect(
853 str
. left_end
- str
. left_start
+ 1 );
854 QRect stareaR
= QRect (
855 desktopArea
. right () - str
. right_width
+ 1,
858 str
. right_end
- str
. right_start
+ 1 );
859 QRect stareaT
= QRect (
862 str
. top_end
- str
. top_start
+ 1,
864 QRect stareaB
= QRect (
866 desktopArea
. bottom () - str
. bottom_width
+ 1,
867 str
. bottom_end
- str
. bottom_start
+ 1,
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())
878 if( stareaR
.right() > screenarea
.right())
880 if( stareaT
.top() < screenarea
.top())
882 if( stareaB
.bottom() < screenarea
.bottom())
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 );
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
922 ext
.left_width
= str
.left
;
924 ext
.left_end
= displayHeight();
928 ext
.right_width
= str
.right
;
930 ext
.right_end
= displayHeight();
934 ext
.top_width
= str
.top
;
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();
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 )
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 )
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()
998 QRect area
= workspace()->clientArea( FullScreenArea
, this );
999 if( geometry() != area
)
1000 setGeometry( area
);
1007 if( workspace()->managingTopMenus())
1010 ClientList mainclients
= mainClients();
1011 if( mainclients
.count() == 1 )
1012 area
= workspace()->clientArea( MaximizeFullArea
, mainclients
.first());
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
);
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())
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());
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())
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());
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
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
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() ;
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
);
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 ) \
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 ) \
1240 int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
1241 if( w + delta <= max_width ) \
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 ) \
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 ) \
1260 int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
1261 if( h + delta <= max_height ) \
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
1278 case SizemodeFixedW
:
1280 // the checks are order so that attempts to modify height are first
1282 ASPECT_CHECK_SHRINK_H_GROW_W
1283 ASPECT_CHECK_SHRINK_W_GROW_H
1287 case SizemodeFixedH
:
1290 ASPECT_CHECK_SHRINK_W_GROW_H
1291 ASPECT_CHECK_SHRINK_H_GROW_W
1297 // first checks that try to shrink
1298 ASPECT_CHECK_SHRINK_H_GROW_W
1299 ASPECT_CHECK_SHRINK_W_GROW_H
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
)
1317 if( maximizeMode() & MaximizeVertical
)
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()
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
;
1353 xSizeHint
.base_width
= xSizeHint
.base_height
= 0;
1354 if( ! ( xSizeHint
.flags
& PMaxSize
))
1355 xSizeHint
.max_width
= xSizeHint
.max_height
= INT_MAX
;
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 );
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 );
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
;
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
))
1399 area
= workspace()->clientArea( WorkArea
, this );
1400 if( area
.contains( orig_geometry
))
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
1423 void Client::sendSyntheticConfigureNotify()
1426 c
.type
= ConfigureNotify
;
1427 c
.send_event
= True
;
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();
1436 c
.override_redirect
= 0;
1437 XSendEvent( display(), c
.event
, true, StructureNotifyMask
, (XEvent
*)&c
);
1440 const QPoint
Client::calculateGravitation( bool invert
, int gravity
) const
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
1451 case NorthWestGravity
: // move down right
1456 case NorthGravity
: // move right
1460 case NorthEastGravity
: // move down left
1464 case WestGravity
: // move right
1469 break; // will be handled specially
1470 case StaticGravity
: // don't move
1474 case EastGravity
: // move left
1478 case SouthWestGravity
: // move up right
1480 dy
= -border_bottom
;
1482 case SouthGravity
: // move up
1484 dy
= -border_bottom
;
1486 case SouthEastGravity
: // move up left
1488 dy
= -border_bottom
;
1491 if( gravity
!= CenterGravity
)
1492 { // translate from client movement to frame movement
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;
1502 return QPoint( x() + dx
, y() + dy
);
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
)
1516 if ( value_mask
& CWY
)
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
1523 if ( new_pos
.x() == x() + clientPos().x() && new_pos
.y() == y() + clientPos().y()
1524 && gravity
== NorthWestGravity
&& !from_tool
)
1530 int nw
= clientSize().width();
1531 int nh
= clientSize().height();
1532 if ( value_mask
& CWWidth
)
1534 if ( value_mask
& CWHeight
)
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
1543 QRect orig_geometry
= geometry();
1544 GeometryUpdatesBlocker
blocker( this );
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
))
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()
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
)
1570 if ( value_mask
& CWHeight
)
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
))
1590 area
= workspace()->clientArea( WorkArea
, this );
1591 if( area
.contains( orig_geometry
))
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
);
1606 if( h
== border_top
+ border_bottom
)
1608 kWarning(1212) << "Shaded geometry passed for size:" ;
1609 kWarning(1212) << kBacktrace() ;
1614 QRect area
= workspace()->clientArea( WorkArea
, this );
1615 // don't allow growing larger than workarea
1616 if( w
> area
.width())
1618 if( h
> area
.height())
1620 QSize tmp
= adjustedSize( QSize( w
, h
)); // checks size constraints, including min/max size
1623 switch( xSizeHint
.win_gravity
)
1625 case NorthWestGravity
: // top left corner doesn't move
1628 case NorthGravity
: // middle of top border doesn't move
1629 newx
= ( newx
+ width() / 2 ) - ( w
/ 2 );
1631 case NorthEastGravity
: // top right corner doesn't move
1632 newx
= newx
+ width() - w
;
1634 case WestGravity
: // middle of left border doesn't move
1635 newy
= ( newy
+ height() / 2 ) - ( h
/ 2 );
1637 case CenterGravity
: // middle point doesn't move
1638 newx
= ( newx
+ width() / 2 ) - ( w
/ 2 );
1639 newy
= ( newy
+ height() / 2 ) - ( h
/ 2 );
1641 case StaticGravity
: // top left corner of _client_ window doesn't move
1642 // since decoration doesn't change, equal to NorthWestGravity
1644 case EastGravity
: // // middle of right border doesn't move
1645 newx
= newx
+ width() - w
;
1646 newy
= ( newy
+ height() / 2 ) - ( h
/ 2 );
1648 case SouthWestGravity
: // bottom left corner doesn't move
1649 newy
= newy
+ height() - h
;
1651 case SouthGravity
: // middle of bottom border doesn't move
1652 newx
= ( newx
+ width() / 2 ) - ( w
/ 2 );
1653 newy
= newy
+ height() - h
;
1655 case SouthEastGravity
: // bottom right corner doesn't move
1656 newx
= newx
+ width() - w
;
1657 newy
= newy
+ height() - h
;
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())
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())
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;
1686 if( flags
& ( 1 << 8 ))
1688 if( flags
& ( 1 << 9 ))
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
1701 bool Client::isMovable() const
1703 if( !motif_may_move
|| isFullScreen())
1705 if( isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :)
1707 if( maximizeMode() == MaximizeFull
&& !options
->moveResizeMaximizedWindows() )
1709 if( rules()->checkPosition( invalidPoint
) != invalidPoint
) // forced position
1715 Returns whether the window is moveable across Xinerama screens
1717 bool Client::isMovableAcrossScreens() const
1719 if( !motif_may_move
)
1721 if( isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :)
1723 if( rules()->checkPosition( invalidPoint
) != invalidPoint
) // forced position
1729 Returns whether the window is resizable or has a fixed size.
1731 bool Client::isResizable() const
1733 if( !motif_may_resize
|| isFullScreen())
1735 if( isSpecialWindow() || isSplash() || isToolbar())
1737 if( maximizeMode() == MaximizeFull
&& !options
->moveResizeMaximizedWindows() )
1739 if( rules()->checkSize( QSize()).isValid()) // forced size
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() ?
1758 if ( maximizeMode() != MaximizeRestore
)
1760 QSize max
= maxSize();
1762 if( max
.width() < 32767 || max
.height() < 32767 ) // sizes are 16bit with X
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())
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
)
1795 if( h
== border_top
+ border_bottom
)
1797 kDebug(1212) << "Shaded geometry passed for size:";
1798 kDebug(1212) << kBacktrace();
1802 client_size
= QSize( w
- border_left
- border_right
, h
- border_top
- border_bottom
);
1803 h
= border_top
+ border_bottom
;
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
)
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
;
1827 pending_geometry_update
= PendingGeometryNormal
;
1830 bool resized
= ( geom_before_block
.size() != geom
.size() || pending_geometry_update
== PendingGeometryForced
);
1833 resizeDecoration( QSize( w
, h
));
1834 XMoveResizeWindow( display(), frameId(), x
, y
, w
, h
);
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());
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();
1855 discardWindowPixmap();
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
)
1873 if( h
== border_top
+ border_bottom
)
1875 kDebug(1212) << "Shaded geometry passed for size:";
1876 kDebug(1212) << kBacktrace();
1880 client_size
= QSize( w
- border_left
- border_right
, h
- border_top
- border_bottom
);
1881 h
= border_top
+ border_bottom
;
1886 client_size
= QSize( w
- border_left
- border_right
, h
- border_top
- border_bottom
);
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
)
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
;
1907 pending_geometry_update
= PendingGeometryNormal
;
1910 resizeDecoration( s
);
1911 XResizeWindow( display(), frameId(), w
, h
);
1912 // resizeDecoration( s );
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());
1921 sendSyntheticConfigureNotify();
1922 updateWindowRules();
1923 checkMaximizeGeometry();
1924 workspace()->checkActiveScreen( this );
1925 workspace()->updateStackingOrder();
1926 workspace()->checkUnredirect();
1927 discardWindowPixmap();
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 );
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
)
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
;
1961 pending_geometry_update
= PendingGeometryNormal
;
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
)
1981 if( block_geometry_updates
== 0 )
1982 pending_geometry_update
= PendingGeometryNone
;
1983 ++block_geometry_updates
;
1987 if( --block_geometry_updates
== 0 )
1989 if( pending_geometry_update
!= PendingGeometryNone
)
1992 setGeometry( QRect( pos(), adjustedSize()), NormalGeometrySet
);
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
2012 max_mode
& MaximizeVertical
? !vertically
: vertically
,
2013 max_mode
& MaximizeHorizontal
? !horizontally
: horizontally
,
2017 void Client::changeMaximize( bool vertical
, bool horizontal
, bool adjust
)
2019 if( !isMaximizable())
2022 MaximizeMode old_mode
= max_mode
;
2023 // 'adjust == true' means to update the size only, e.g. after changing workspace size
2027 max_mode
= MaximizeMode( max_mode
^ MaximizeVertical
);
2029 max_mode
= MaximizeMode( max_mode
^ MaximizeHorizontal
);
2032 max_mode
= rules()->checkMaximize( max_mode
);
2033 if( !adjust
&& max_mode
== old_mode
)
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());
2063 if(( vertical
&& !(old_mode
& MaximizeVertical
))
2064 || ( horizontal
&& !( old_mode
& MaximizeHorizontal
)))
2065 Notify::raise( Notify::Maximize
);
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
;
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
);
2106 setGeometry( QRect(QPoint( geom_restore
.x(), clientArea
.top()),
2107 adjustedSize(QSize( geom_restore
.width(), clientArea
.height()), SizemodeFixedH
)), geom_mode
);
2112 setGeometry( QRect(QPoint(x(), clientArea
.top()),
2113 adjustedSize(QSize(width(), clientArea
.height()), SizemodeFixedH
)), geom_mode
);
2115 info
->setState( NET::MaxVert
, NET::Max
);
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
);
2130 setGeometry( QRect( QPoint(clientArea
.left(), geom_restore
.y()),
2131 adjustedSize(QSize(clientArea
.width(), geom_restore
.height()), SizemodeFixedW
)), geom_mode
);
2136 setGeometry( QRect( QPoint(clientArea
.left(), y()),
2137 adjustedSize(QSize(clientArea
.width(), height()), SizemodeFixedW
)), geom_mode
);
2139 info
->setState( NET::MaxHoriz
, NET::Max
);
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
);
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
);
2198 updateAllowedActions();
2199 if( decoration
!= NULL
)
2200 decoration
->maximizeChange();
2201 updateWindowRules();
2204 void Client::resetMaximize()
2206 if( max_mode
== MaximizeRestore
)
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
);
2215 setGeometry( QRect( pos(), sizeForClientSize( clientSize())), ForceGeometrySet
);
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
2228 if( isMove() || isResize()) // this is because of the option to disallow moving/resizing of max-ed windows
2231 static int recursion_protection
= 0;
2232 if( recursion_protection
> 3 )
2234 kWarning( 1212 ) << "Check maximize overflow - you loose!" ;
2235 kWarning( 1212 ) << kBacktrace() ;
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 ))
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())
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
)
2283 if( !isFullScreenable( 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
)
2294 if( fullscreen_mode
== FullScreenHack
)
2296 if( user
&& !userCanSetFullScreen())
2298 set
= rules()->checkFullScreen( set
);
2299 setShade( ShadeNone
);
2300 bool was_fs
= isFullScreen();
2302 geom_fs_restore
= geometry();
2303 fullscreen_mode
= set
? FullScreenNormal
: FullScreenNone
;
2304 if( was_fs
== isFullScreen())
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 );
2312 if( info
->fullscreenMonitors().isSet())
2313 setGeometry( fullscreenMonitorsArea( info
->fullscreenMonitors()));
2315 setGeometry( workspace()->clientArea( FullScreenArea
, this ));
2318 if( !geom_fs_restore
.isNull())
2319 setGeometry( QRect( geom_fs_restore
.topLeft(), adjustedSize( geom_fs_restore
.size())));
2320 // TODO isShaded() ?
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.";
2348 info
->setFullscreenMonitors( topology
);
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;
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
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 );
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());
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
)
2430 doDrawbound( *visible_bound
, true );
2431 delete visible_bound
;
2435 void Client::doDrawbound( const QRect
& geom
, bool clear
)
2437 if( decoration
!= NULL
&& decoration
->drawbound( geom
, clear
))
2438 return; // done by decoration
2440 xgc
.function
= GXxor
;
2441 xgc
.foreground
= WhitePixel( display(), DefaultScreen( display()));
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)
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())
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()));
2479 wgeom
.setHeight( 0 );
2480 geometryTip
->setGeometry( wgeom
);
2481 if( !geometryTip
->isVisible())
2482 geometryTip
->show();
2483 geometryTip
->raise();
2487 class EatAllPaintEvents
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
)
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
;
2526 if ( maximizeMode() != MaximizeRestore
)
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
) )
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
);
2547 static_cast<EffectsHandlerImpl
*>(effects
)->windowUserMovedResized( effectWindow(), true, false );
2548 if( options
->electricBorders() == Options::ElectricMoveOnly
)
2549 workspace()->reserveElectricBorderSwitching( true );
2553 void Client::finishMoveResize( bool cancel
)
2557 setGeometry( initialMoveResizeGeom
);
2559 setGeometry( moveResizeGeom
);
2560 checkMaximizeGeometry();
2562 Notify::raise( isResize() ? Notify::ResizeEnd
: Notify::MoveEnd
);
2564 static_cast<EffectsHandlerImpl
*>(effects
)->windowUserMovedResized( effectWindow(), false, true );
2567 void Client::leaveMoveResize()
2572 geometryTip
->hide();
2576 if ( ( isMove() && rules()->checkMoveResizeMode( options
->moveMode
) != Options::Opaque
)
2577 || ( isResize() && rules()->checkMoveResizeMode( options
->resizeMode
) != Options::Opaque
) )
2579 if( move_resize_has_keyboard_grab
)
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;
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
)
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
;
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;
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())
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())))
2676 if ( !moveResizeMode
)
2678 QPoint
p( QPoint( x
, y
) - moveOffset
);
2679 if (p
.manhattanLength() >= 6)
2681 if( !startMoveResize())
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?
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;
2726 // first resize (without checking constrains), then snap, then check bounds, then check constrains
2727 QRect orig
= initialMoveResizeGeom
;
2728 Sizemode sizemode
= SizemodeAny
;
2731 case PositionTopLeft
:
2732 moveResizeGeom
= QRect( topleft
, orig
.bottomRight() ) ;
2734 case PositionBottomRight
:
2735 moveResizeGeom
= QRect( orig
.topLeft(), bottomright
) ;
2737 case PositionBottomLeft
:
2738 moveResizeGeom
= QRect( QPoint( topleft
.x(), orig
.y() ), QPoint( orig
.right(), bottomright
.y()) ) ;
2740 case PositionTopRight
:
2741 moveResizeGeom
= QRect( QPoint( orig
.x(), topleft
.y() ), QPoint( bottomright
.x(), orig
.bottom()) ) ;
2744 moveResizeGeom
= QRect( QPoint( orig
.left(), topleft
.y() ), orig
.bottomRight() ) ;
2745 sizemode
= SizemodeFixedH
; // try not to affect height
2747 case PositionBottom
:
2748 moveResizeGeom
= QRect( orig
.topLeft(), QPoint( orig
.right(), bottomright
.y() ) ) ;
2749 sizemode
= SizemodeFixedH
;
2752 moveResizeGeom
= QRect( QPoint( topleft
.x(), orig
.top() ), orig
.bottomRight() ) ;
2753 sizemode
= SizemodeFixedW
;
2756 moveResizeGeom
= QRect( orig
.topLeft(), QPoint( bottomright
.x(), orig
.bottom() ) ) ;
2757 sizemode
= SizemodeFixedW
;
2759 case PositionCenter
:
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
;
2786 { // these 4 corners ones are copied from above
2787 case PositionTopLeft
:
2788 moveResizeGeom
= QRect( topleft
, orig
.bottomRight() ) ;
2790 case PositionBottomRight
:
2791 moveResizeGeom
= QRect( orig
.topLeft(), bottomright
) ;
2793 case PositionBottomLeft
:
2794 moveResizeGeom
= QRect( QPoint( topleft
.x(), orig
.y() ), QPoint( orig
.right(), bottomright
.y()) ) ;
2796 case PositionTopRight
:
2797 moveResizeGeom
= QRect( QPoint( orig
.x(), topleft
.y() ), QPoint( bottomright
.x(), orig
.bottom()) ) ;
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 ?
2803 moveResizeGeom
= QRect( QPoint( orig
.left(), topleft
.y() ), QPoint( bottomright
.x(), orig
.bottom()) ) ;
2805 case PositionBottom
:
2806 moveResizeGeom
= QRect( orig
.topLeft(), QPoint( bottomright
.x(), bottomright
.y() ) ) ;
2809 moveResizeGeom
= QRect( QPoint( topleft
.x(), orig
.top() ), QPoint( orig
.right(), bottomright
.y()));
2812 moveResizeGeom
= QRect( orig
.topLeft(), QPoint( bottomright
.x(), bottomright
.y() ) ) ;
2814 case PositionCenter
:
2819 if( moveResizeGeom
.size() != previousMoveResizeGeom
.size())
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 );
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())
2855 if( sync_timeout
!= NULL
)
2857 sync_resize_pending
= true;
2863 performMoveResize();
2865 workspace()->checkElectricBorder(globalPos
, xTime());
2868 void Client::performMoveResize()
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 );
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
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();