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"
43 #include <QDesktopWidget>
48 //********************************************
50 //********************************************
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
);
64 destroyElectricBorders();
65 updateElectricBorders();
69 QTimer::singleShot( 0, this, SLOT( setupCompositing() ) );
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
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 ();
98 screens
[iS
] = desktopwidget
-> screenGeometry (iS
);
101 i
<= numberOfDesktops();
104 new_wareas
[ i
] = desktopArea
;
105 new_sareas
[ i
] = new QRect
[ nscreens
];
109 new_sareas
[ i
][ iS
] = screens
[ iS
];
111 for ( ClientList::ConstIterator it
= clients
.begin(); it
!= clients
.end(); ++it
)
113 if( !(*it
)->hasStrut())
115 QRect r
= (*it
)->adjustedClientArea( desktopArea
, desktopArea
);
116 if( (*it
)->isOnAllDesktops())
118 i
<= numberOfDesktops();
121 new_wareas
[ i
] = new_wareas
[ i
].intersected( r
);
125 new_sareas
[ i
][ iS
] =
126 new_sareas
[ i
][ iS
].intersected(
127 (*it
)->adjustedClientArea( desktopArea
, screens
[ iS
] )
132 new_wareas
[ (*it
)->desktop() ] = new_wareas
[ (*it
)->desktop() ].intersected( r
);
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
] )
147 i
<= numberOfDesktops();
153 kDebug () << "new_sarea: " << new_sareas
[ i
][ iS
];
156 // TODO topmenu update for screenarea changes?
157 if( topmenu_space
!= NULL
)
159 QRect topmenu_area
= desktopArea
;
160 topmenu_area
.setTop( topMenuHeight());
162 i
<= numberOfDesktops();
164 new_wareas
[ i
] = new_wareas
[ i
].intersected( topmenu_area
);
167 bool changed
= force
;
173 !changed
&& i
<= numberOfDesktops();
176 if( workarea
[ i
] != new_wareas
[ i
] )
181 if (new_sareas
[ i
][ iS
] != screenarea
[ i
][ iS
])
188 workarea
= new_wareas
;
191 screenarea
= new_sareas
;
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();
207 (*it
)->checkWorkspacePosition();
208 for( ClientList::ConstIterator it
= desktops
.begin();
209 it
!= desktops
.end();
211 (*it
)->checkWorkspacePosition();
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().
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
];
245 if (options
->xineramaMaximizeEnabled
)
249 case MaximizeFullArea
:
250 if (options
->xineramaMaximizeEnabled
)
251 return desktopwidget
->screenGeometry( screen
);
253 return desktopwidget
->geometry();
255 if (options
->xineramaFullscreenEnabled
)
256 return desktopwidget
->screenGeometry( screen
);
258 return desktopwidget
->geometry();
260 if (options
->xineramaPlacementEnabled
)
265 if (options
->xineramaMovementEnabled
)
266 return desktopwidget
->screenGeometry( screen
);
268 return desktopwidget
->geometry();
272 return desktopwidget
->geometry();
274 return desktopwidget
->screenGeometry( screen
);
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();
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());
319 const int ry(cy
+ch
); //these don't change
321 int nx(cx
), ny(cy
); //buffers
323 int deltaY(ymax
); //minimum distance to other clients
325 int lx
, ly
, lrx
, lry
; //coords and size for the comparison client, l
328 int snap
= options
->borderSnapZone
; //snap trigger
331 if ((sOWO
?(cx
<xmin
):true) && (qAbs(xmin
-cx
)<snap
))
336 if ((sOWO
?(rx
>xmax
):true) && (qAbs(rx
-xmax
)<snap
) && (qAbs(xmax
-rx
) < deltaX
))
342 if ((sOWO
?(cy
<ymin
):true) && (qAbs(ymin
-cy
)<snap
))
347 if ((sOWO
?(ry
>ymax
):true) && (qAbs(ry
-ymax
)<snap
) && (qAbs(ymax
-ry
) < deltaY
))
355 snap
= options
->windowSnapZone
;
358 QList
<Client
*>::ConstIterator l
;
359 for (l
= clients
.begin();l
!= clients
.end();++l
)
361 if ((*l
)->isOnDesktop(currentDesktop()) &&
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
);
379 if ((sOWO
?(rx
>lx
):true) && (qAbs(rx
-lx
)<snap
) && ( qAbs( rx
- lx
)<deltaX
) )
381 deltaX
= qAbs(rx
- lx
);
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
);
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
);
405 pos
= QPoint(nx
, ny
);
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
);
433 int deltaY(ymax
); //minimum distance to other clients
435 int lx
, ly
, lrx
, lry
; //coords and size for the comparison client, l
438 int snap
= options
->borderSnapZone
; //snap trigger
444 #define SNAP_BORDER_TOP \
445 if ((sOWO?(newcy<ymin):true) && (qAbs(ymin-newcy)<deltaY)) \
447 deltaY = qAbs(ymin-newcy); \
451 #define SNAP_BORDER_BOTTOM \
452 if ((sOWO?(newry>ymax):true) && (qAbs(ymax-newry)<deltaY)) \
454 deltaY = qAbs(ymax-newcy); \
458 #define SNAP_BORDER_LEFT \
459 if ((sOWO?(newcx<xmin):true) && (qAbs(xmin-newcx)<deltaX)) \
461 deltaX = qAbs(xmin-newcx); \
465 #define SNAP_BORDER_RIGHT \
466 if ((sOWO?(newrx>xmax):true) && (qAbs(xmax-newrx)<deltaX)) \
468 deltaX = qAbs(xmax-newrx); \
473 case PositionBottomRight
:
483 case PositionTopLeft
:
493 case PositionTopRight
:
497 case PositionBottomLeft
:
510 snap
= options
->windowSnapZone
;
515 QList
<Client
*>::ConstIterator l
;
516 for (l
= clients
.begin();l
!= clients
.end();++l
)
518 if ((*l
)->isOnDesktop(currentDesktop()) &&
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) \
537 && (qAbs( lry - newcy ) < deltaY) ) { \
538 deltaY = qAbs( lry - newcy ); \
542 #define SNAP_WINDOW_BOTTOM if ( (sOWO?(newry>ly):true) \
544 && (qAbs( ly - newry ) < deltaY) ) { \
545 deltaY = qAbs( ly - newry ); \
549 #define SNAP_WINDOW_LEFT if ( (sOWO?(newcx<lrx):true) \
551 && (qAbs( lrx - newcx ) < deltaX)) { \
552 deltaX = qAbs( lrx - newcx ); \
556 #define SNAP_WINDOW_RIGHT if ( (sOWO?(newrx>lx):true) \
558 && (qAbs( lx - newrx ) < deltaX)) \
560 deltaX = qAbs( lx - newrx ); \
566 case PositionBottomRight
:
576 case PositionTopLeft
:
586 case PositionTopRight
:
590 case PositionBottomLeft
:
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.
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()) )
637 initPositioning
->placeCascaded(*it
, area
);
642 Unclutters the current desktop by smart-placing all clients
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()) )
654 initPositioning
->placeSmart(clients
.at( i
), QRect());
659 void Workspace::updateTopMenuGeometry( Client
* c
)
661 if( !managingTopMenus())
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();
682 // c == NULL - update all, including topmenu_space
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();
690 updateTopMenuGeometry( *it
);
693 //********************************************
695 //********************************************
698 void Client::keepInArea( QRect area
, bool 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() ))
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
734 // topmenu area is reserved in updateClientArea()
737 NETExtendedStrut str
= strut();
738 QRect stareaL
= QRect(
742 str
. left_end
- str
. left_start
+ 1 );
743 QRect stareaR
= QRect (
744 desktopArea
. right () - str
. right_width
+ 1,
747 str
. right_end
- str
. right_start
+ 1 );
748 QRect stareaT
= QRect (
751 str
. top_end
- str
. top_start
+ 1,
753 QRect stareaB
= QRect (
755 desktopArea
. bottom () - str
. bottom_width
+ 1,
756 str
. bottom_end
- str
. bottom_start
+ 1,
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())
767 if( stareaR
.right() > screenarea
.right())
769 if( stareaT
.top() < screenarea
.top())
771 if( stareaB
.bottom() < screenarea
.bottom())
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 );
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
811 ext
.left_width
= str
.left
;
813 ext
.left_end
= displayHeight();
817 ext
.right_width
= str
.right
;
819 ext
.right_end
= displayHeight();
823 ext
.top_width
= str
.top
;
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();
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 )
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 )
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()
885 QRect area
= workspace()->clientArea( FullArea
, this );
886 if( geometry() != area
)
892 QRect area
= workspace()->clientArea( FullScreenArea
, this );
893 if( geometry() != area
)
901 if( workspace()->managingTopMenus())
904 ClientList mainclients
= mainClients();
905 if( mainclients
.count() == 1 )
906 area
= workspace()->clientArea( MaximizeFullArea
, mainclients
.first());
908 area
= workspace()->clientArea( MaximizeFullArea
, QPoint( 0, 0 ), desktop());
909 area
.setHeight( workspace()->topMenuHeight());
910 // kDebug() << "TOPMENU size adjust: " << area << ":" << this;
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())
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());
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())
991 rect
.setLeft( area
.left() + ( -old_diff
- 1 ) );
993 rect
.setRight( area
.right() - ( old_diff
- 1 ));
995 if( rect
.width() > area
.width() && isResizable())
996 rect
.setWidth( area
.width());
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
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
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() ;
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
);
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 ) \
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 ) \
1134 int delta = int( min_aspect_w * h / min_aspect_h - w ) / width_inc * width_inc; \
1135 if( w + delta <= max_width ) \
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 ) \
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 ) \
1154 int delta = int( w * max_aspect_h / max_aspect_w - h ) / height_inc * height_inc; \
1155 if( h + delta <= max_height ) \
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
1172 case SizemodeFixedW
:
1174 // the checks are order so that attempts to modify height are first
1176 ASPECT_CHECK_SHRINK_H_GROW_W
1177 ASPECT_CHECK_SHRINK_W_GROW_H
1181 case SizemodeFixedH
:
1184 ASPECT_CHECK_SHRINK_W_GROW_H
1185 ASPECT_CHECK_SHRINK_H_GROW_W
1191 // first checks that try to shrink
1192 ASPECT_CHECK_SHRINK_H_GROW_W
1193 ASPECT_CHECK_SHRINK_W_GROW_H
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
)
1211 if( maximizeMode() & MaximizeVertical
)
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()
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
;
1247 xSizeHint
.base_width
= xSizeHint
.base_height
= 0;
1248 if( ! ( xSizeHint
.flags
& PMaxSize
))
1249 xSizeHint
.max_width
= xSizeHint
.max_height
= INT_MAX
;
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 );
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 );
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
;
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
))
1293 area
= workspace()->clientArea( WorkArea
, this );
1294 if( area
.contains( orig_geometry
))
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
1317 void Client::sendSyntheticConfigureNotify()
1320 c
.type
= ConfigureNotify
;
1321 c
.send_event
= True
;
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();
1330 c
.override_redirect
= 0;
1331 XSendEvent( display(), c
.event
, true, StructureNotifyMask
, (XEvent
*)&c
);
1334 const QPoint
Client::calculateGravitation( bool invert
, int gravity
) const
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
1345 case NorthWestGravity
: // move down right
1350 case NorthGravity
: // move right
1354 case NorthEastGravity
: // move down left
1358 case WestGravity
: // move right
1363 break; // will be handled specially
1364 case StaticGravity
: // don't move
1368 case EastGravity
: // move left
1372 case SouthWestGravity
: // move up right
1374 dy
= -border_bottom
;
1376 case SouthGravity
: // move up
1378 dy
= -border_bottom
;
1380 case SouthEastGravity
: // move up left
1382 dy
= -border_bottom
;
1385 if( gravity
!= CenterGravity
)
1386 { // translate from client movement to frame movement
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;
1396 return QPoint( x() + dx
, y() + dy
);
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
)
1410 if ( value_mask
& CWY
)
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
1417 if ( new_pos
.x() == x() + clientPos().x() && new_pos
.y() == y() + clientPos().y()
1418 && gravity
== NorthWestGravity
&& !from_tool
)
1424 int nw
= clientSize().width();
1425 int nh
= clientSize().height();
1426 if ( value_mask
& CWWidth
)
1428 if ( value_mask
& CWHeight
)
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
1437 QRect orig_geometry
= geometry();
1438 GeometryUpdatesBlocker
blocker( this );
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
))
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()
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
)
1464 if ( value_mask
& CWHeight
)
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
))
1484 area
= workspace()->clientArea( WorkArea
, this );
1485 if( area
.contains( orig_geometry
))
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
)
1501 if( h
== border_top
+ border_bottom
)
1503 kWarning() << "Shaded geometry passed for size:" ;
1504 kWarning() << kBacktrace() ;
1509 QRect area
= workspace()->clientArea( WorkArea
, this );
1510 // don't allow growing larger than workarea
1511 if( w
> area
.width())
1513 if( h
> area
.height())
1515 QSize tmp
= adjustedSize( QSize( w
, h
)); // checks size constraints, including min/max size
1518 switch( xSizeHint
.win_gravity
)
1520 case NorthWestGravity
: // top left corner doesn't move
1523 case NorthGravity
: // middle of top border doesn't move
1524 newx
= ( newx
+ width() / 2 ) - ( w
/ 2 );
1526 case NorthEastGravity
: // top right corner doesn't move
1527 newx
= newx
+ width() - w
;
1529 case WestGravity
: // middle of left border doesn't move
1530 newy
= ( newy
+ height() / 2 ) - ( h
/ 2 );
1532 case CenterGravity
: // middle point doesn't move
1533 newx
= ( newx
+ width() / 2 ) - ( w
/ 2 );
1534 newy
= ( newy
+ height() / 2 ) - ( h
/ 2 );
1536 case StaticGravity
: // top left corner of _client_ window doesn't move
1537 // since decoration doesn't change, equal to NorthWestGravity
1539 case EastGravity
: // // middle of right border doesn't move
1540 newx
= newx
+ width() - w
;
1541 newy
= ( newy
+ height() / 2 ) - ( h
/ 2 );
1543 case SouthWestGravity
: // bottom left corner doesn't move
1544 newy
= newy
+ height() - h
;
1546 case SouthGravity
: // middle of bottom border doesn't move
1547 newx
= ( newx
+ width() / 2 ) - ( w
/ 2 );
1548 newy
= newy
+ height() - h
;
1550 case SouthEastGravity
: // bottom right corner doesn't move
1551 newx
= newx
+ width() - w
;
1552 newy
= newy
+ height() - h
;
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())
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())
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;
1581 if( flags
& ( 1 << 8 ))
1583 if( flags
& ( 1 << 9 ))
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
1596 bool Client::isMovable() const
1598 if( !motif_may_move
|| isFullScreen())
1600 if( isSpecialWindow() && !isSplash() && !isToolbar()) // allow moving of splashscreens :)
1602 if( maximizeMode() == MaximizeFull
&& !options
->moveResizeMaximizedWindows() )
1604 if( rules()->checkPosition( invalidPoint
) != invalidPoint
) // forced position
1610 Returns whether the window is resizable or has a fixed size.
1612 bool Client::isResizable() const
1614 if( !motif_may_resize
|| isFullScreen())
1616 if( isSpecialWindow() || isSplash() || isToolbar())
1618 if( maximizeMode() == MaximizeFull
&& !options
->moveResizeMaximizedWindows() )
1620 if( rules()->checkSize( QSize()).isValid()) // forced size
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() ?
1639 if ( maximizeMode() != MaximizeRestore
)
1641 QSize max
= maxSize();
1643 if( max
.width() < 32767 || max
.height() < 32767 ) // sizes are 16bit with X
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())
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
)
1676 if( h
== border_top
+ border_bottom
)
1678 kDebug() << "Shaded geometry passed for size:";
1679 kDebug() << kBacktrace();
1683 client_size
= QSize( w
- border_left
- border_right
, h
- border_top
- border_bottom
);
1684 h
= border_top
+ border_bottom
;
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
)
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
;
1707 pending_geometry_update
= PendingGeometryNormal
;
1710 bool resized
= ( geom_before_block
.size() != geom
.size() || pending_geometry_update
== PendingGeometryForced
);
1713 resizeDecoration( QSize( w
, h
));
1714 XMoveResizeWindow( display(), frameId(), x
, y
, w
, h
);
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());
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 );
1734 discardWindowPixmap();
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
)
1752 if( h
== border_top
+ border_bottom
)
1754 kDebug() << "Shaded geometry passed for size:";
1755 kDebug() << kBacktrace();
1759 client_size
= QSize( w
- border_left
- border_right
, h
- border_top
- border_bottom
);
1760 h
= border_top
+ border_bottom
;
1765 client_size
= QSize( w
- border_left
- border_right
, h
- border_top
- border_bottom
);
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
)
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
;
1785 pending_geometry_update
= PendingGeometryNormal
;
1788 resizeDecoration( s
);
1789 XResizeWindow( display(), frameId(), w
, h
);
1790 // resizeDecoration( s );
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());
1799 updateWorkareaDiffs();
1800 sendSyntheticConfigureNotify();
1801 updateWindowRules();
1802 checkMaximizeGeometry();
1803 workspace()->checkActiveScreen( this );
1804 discardWindowPixmap();
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 );
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
)
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
;
1837 pending_geometry_update
= PendingGeometryNormal
;
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
)
1856 if( block_geometry_updates
== 0 )
1857 pending_geometry_update
= PendingGeometryNone
;
1858 ++block_geometry_updates
;
1862 if( --block_geometry_updates
== 0 )
1864 if( pending_geometry_update
!= PendingGeometryNone
)
1867 setGeometry( QRect( pos(), adjustedSize()), NormalGeometrySet
);
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
1887 max_mode
& MaximizeVertical
? !vertically
: vertically
,
1888 max_mode
& MaximizeHorizontal
? !horizontally
: horizontally
,
1892 void Client::changeMaximize( bool vertical
, bool horizontal
, bool adjust
)
1894 if( !isMaximizable())
1897 MaximizeMode old_mode
= max_mode
;
1898 // 'adjust == true' means to update the size only, e.g. after changing workspace size
1902 max_mode
= MaximizeMode( max_mode
^ MaximizeVertical
);
1904 max_mode
= MaximizeMode( max_mode
^ MaximizeHorizontal
);
1907 max_mode
= rules()->checkMaximize( max_mode
);
1908 if( !adjust
&& max_mode
== old_mode
)
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());
1938 if(( vertical
&& !(old_mode
& MaximizeVertical
))
1939 || ( horizontal
&& !( old_mode
& MaximizeHorizontal
)))
1940 Notify::raise( Notify::Maximize
);
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
;
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
);
1976 setGeometry( QRect(QPoint( geom_restore
.x(), clientArea
.top()),
1977 adjustedSize(QSize( geom_restore
.width(), clientArea
.height()), SizemodeFixedH
)));
1980 setGeometry( QRect(QPoint(x(), clientArea
.top()),
1981 adjustedSize(QSize(width(), clientArea
.height()), SizemodeFixedH
)));
1982 info
->setState( NET::MaxVert
, NET::Max
);
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
);
1996 setGeometry( QRect( QPoint(clientArea
.left(), geom_restore
.y()),
1997 adjustedSize(QSize(clientArea
.width(), geom_restore
.height()), SizemodeFixedW
)));
2000 setGeometry( QRect( QPoint(clientArea
.left(), y()),
2001 adjustedSize(QSize(clientArea
.width(), height()), SizemodeFixedW
)));
2002 info
->setState( NET::MaxHoriz
, NET::Max
);
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
);
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
);
2052 info
->setState( NET::Max
, NET::Max
);
2059 updateAllowedActions();
2060 if( decoration
!= NULL
)
2061 decoration
->maximizeChange();
2062 updateWindowRules();
2065 void Client::resetMaximize()
2067 if( max_mode
== MaximizeRestore
)
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
);
2076 setGeometry( QRect( pos(), sizeForClientSize( clientSize())), ForceGeometrySet
);
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
2089 if( isMove() || isResize()) // this is because of the option to disallow moving/resizing of max-ed windows
2092 static int recursion_protection
= 0;
2093 if( recursion_protection
> 3 )
2095 kWarning( 1212 ) << "Check maximize overflow - you loose!" ;
2096 kWarning( 1212 ) << kBacktrace() ;
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 ))
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())
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
)
2144 if( !isFullScreenable( 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
)
2155 if( fullscreen_mode
== FullScreenHack
)
2157 if( user
&& !userCanSetFullScreen())
2159 set
= rules()->checkFullScreen( set
);
2160 setShade( ShadeNone
);
2161 bool was_fs
= isFullScreen();
2163 geom_fs_restore
= geometry();
2164 fullscreen_mode
= set
? FullScreenNormal
: FullScreenNone
;
2165 if( was_fs
== isFullScreen())
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 );
2173 setGeometry( workspace()->clientArea( FullScreenArea
, this ));
2176 if( !geom_fs_restore
.isNull())
2177 setGeometry( QRect( geom_fs_restore
.topLeft(), adjustedSize( geom_fs_restore
.size())));
2178 // TODO isShaded() ?
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
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 );
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());
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
)
2242 doDrawbound( *visible_bound
, true );
2243 delete visible_bound
;
2247 void Client::doDrawbound( const QRect
& geom
, bool clear
)
2249 if( decoration
!= NULL
&& decoration
->drawbound( geom
, clear
))
2250 return; // done by decoration
2252 xgc
.function
= GXxor
;
2253 xgc
.foreground
= WhitePixel( display(), DefaultScreen( display()));
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)
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())
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()));
2291 wgeom
.setHeight( 0 );
2292 geometryTip
->setGeometry( wgeom
);
2293 if( !geometryTip
->isVisible())
2295 geometryTip
->show();
2296 geometryTip
->raise();
2301 class EatAllPaintEvents
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
)
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
;
2340 if ( maximizeMode() != MaximizeRestore
)
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
) )
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
);
2361 static_cast<EffectsHandlerImpl
*>(effects
)->windowUserMovedResized( effectWindow(), true, false );
2362 if( options
->electricBorders() == Options::ElectricMoveOnly
)
2363 workspace()->reserveElectricBorderSwitching( true );
2367 void Client::finishMoveResize( bool cancel
)
2371 setGeometry( initialMoveResizeGeom
);
2373 setGeometry( moveResizeGeom
);
2374 checkMaximizeGeometry();
2376 Notify::raise( isResize() ? Notify::ResizeEnd
: Notify::MoveEnd
);
2378 static_cast<EffectsHandlerImpl
*>(effects
)->windowUserMovedResized( effectWindow(), false, true );
2381 void Client::leaveMoveResize()
2386 geometryTip
->hide();
2390 if ( ( isMove() && rules()->checkMoveResizeMode( options
->moveMode
) != Options::Opaque
)
2391 || ( isResize() && rules()->checkMoveResizeMode( options
->resizeMode
) != Options::Opaque
) )
2393 if( move_resize_has_keyboard_grab
)
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;
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
)
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
;
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;
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())
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())))
2490 if ( !moveResizeMode
)
2492 QPoint
p( QPoint( x
, y
) - moveOffset
);
2493 if (p
.manhattanLength() >= 6)
2495 if( !startMoveResize())
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?
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;
2540 // first resize (without checking constrains), then snap, then check bounds, then check constrains
2541 QRect orig
= initialMoveResizeGeom
;
2542 Sizemode sizemode
= SizemodeAny
;
2545 case PositionTopLeft
:
2546 moveResizeGeom
= QRect( topleft
, orig
.bottomRight() ) ;
2548 case PositionBottomRight
:
2549 moveResizeGeom
= QRect( orig
.topLeft(), bottomright
) ;
2551 case PositionBottomLeft
:
2552 moveResizeGeom
= QRect( QPoint( topleft
.x(), orig
.y() ), QPoint( orig
.right(), bottomright
.y()) ) ;
2554 case PositionTopRight
:
2555 moveResizeGeom
= QRect( QPoint( orig
.x(), topleft
.y() ), QPoint( bottomright
.x(), orig
.bottom()) ) ;
2558 moveResizeGeom
= QRect( QPoint( orig
.left(), topleft
.y() ), orig
.bottomRight() ) ;
2559 sizemode
= SizemodeFixedH
; // try not to affect height
2561 case PositionBottom
:
2562 moveResizeGeom
= QRect( orig
.topLeft(), QPoint( orig
.right(), bottomright
.y() ) ) ;
2563 sizemode
= SizemodeFixedH
;
2566 moveResizeGeom
= QRect( QPoint( topleft
.x(), orig
.top() ), orig
.bottomRight() ) ;
2567 sizemode
= SizemodeFixedW
;
2570 moveResizeGeom
= QRect( orig
.topLeft(), QPoint( bottomright
.x(), orig
.bottom() ) ) ;
2571 sizemode
= SizemodeFixedW
;
2573 case PositionCenter
:
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
;
2600 { // these 4 corners ones are copied from above
2601 case PositionTopLeft
:
2602 moveResizeGeom
= QRect( topleft
, orig
.bottomRight() ) ;
2604 case PositionBottomRight
:
2605 moveResizeGeom
= QRect( orig
.topLeft(), bottomright
) ;
2607 case PositionBottomLeft
:
2608 moveResizeGeom
= QRect( QPoint( topleft
.x(), orig
.y() ), QPoint( orig
.right(), bottomright
.y()) ) ;
2610 case PositionTopRight
:
2611 moveResizeGeom
= QRect( QPoint( orig
.x(), topleft
.y() ), QPoint( bottomright
.x(), orig
.bottom()) ) ;
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 ?
2617 moveResizeGeom
= QRect( QPoint( orig
.left(), topleft
.y() ), QPoint( bottomright
.x(), orig
.bottom()) ) ;
2619 case PositionBottom
:
2620 moveResizeGeom
= QRect( orig
.topLeft(), QPoint( bottomright
.x(), bottomright
.y() ) ) ;
2623 moveResizeGeom
= QRect( QPoint( topleft
.x(), orig
.top() ), QPoint( orig
.right(), bottomright
.y()));
2626 moveResizeGeom
= QRect( orig
.topLeft(), QPoint( bottomright
.x(), bottomright
.y() ) ) ;
2628 case PositionCenter
:
2633 if( moveResizeGeom
.size() != previousMoveResizeGeom
.size())
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())
2660 if( sync_timeout
!= NULL
)
2662 sync_resize_pending
= true;
2668 performMoveResize();
2670 workspace()->checkElectricBorder(globalPos
, xTime());
2673 void Client::performMoveResize()
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 );
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
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();