2017-01-02 17:05:30 -05:00
/*
* Copyright 2016 Smith AR < audoban @ openmailbox . org >
* Michail Vourlakos < mvourlakos @ gmail . com >
*
* This file is part of Latte - Dock
*
* Latte - Dock is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License , or ( at your option ) any later version .
*
* Latte - Dock is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2016-12-25 09:25:27 +02:00
# include "xwindowinterface.h"
2018-03-28 20:39:52 +03:00
# include "dock/dockview.h"
# include "dock/screenedgeghostwindow.h"
2016-12-29 00:37:53 -05:00
# include "../liblattedock/extras.h"
2016-12-25 09:25:27 +02:00
2016-12-29 00:37:53 -05:00
# include <QDebug>
2017-02-11 02:54:03 -05:00
# include <QTimer>
2016-12-28 02:57:31 -05:00
# include <QtX11Extras/QX11Info>
2016-12-25 09:25:27 +02:00
# include <KWindowSystem>
2016-12-30 02:11:42 -05:00
# include <KWindowInfo>
2016-12-28 02:57:31 -05:00
# include <NETWM>
2016-12-25 09:25:27 +02:00
2018-03-28 20:39:52 +03:00
# include <xcb/xcb.h>
2016-12-28 02:57:31 -05:00
namespace Latte {
2016-12-25 09:25:27 +02:00
2017-02-24 21:40:47 -05:00
XWindowInterface : : XWindowInterface ( QObject * parent )
: AbstractWindowInterface ( parent )
2016-12-25 09:25:27 +02:00
{
2017-02-24 21:40:47 -05:00
m_activities = new KActivities : : Consumer ( this ) ;
connect ( KWindowSystem : : self ( ) , & KWindowSystem : : activeWindowChanged
2017-03-01 15:26:46 +02:00
, this , & AbstractWindowInterface : : activeWindowChanged ) ;
2017-02-24 21:40:47 -05:00
connect ( KWindowSystem : : self ( )
2017-03-01 15:26:46 +02:00
, static_cast < void ( KWindowSystem : : * ) ( WId , NET : : Properties , NET : : Properties2 ) >
( & KWindowSystem : : windowChanged )
, this , & XWindowInterface : : windowChangedProxy ) ;
2017-02-24 21:40:47 -05:00
2017-06-08 17:10:49 -05:00
auto addWindow = [ & ] ( WindowId wid ) {
2017-01-02 01:04:10 -05:00
if ( std : : find ( m_windows . cbegin ( ) , m_windows . cend ( ) , wid ) = = m_windows . cend ( ) ) {
2017-06-08 17:10:49 -05:00
if ( isValidWindow ( KWindowInfo ( wid . value < WId > ( ) , NET : : WMWindowType ) ) ) {
2017-01-02 01:04:10 -05:00
m_windows . push_back ( wid ) ;
emit windowAdded ( wid ) ;
}
2016-12-28 02:57:31 -05:00
}
2017-01-02 01:04:10 -05:00
} ;
2017-02-24 21:40:47 -05:00
connect ( KWindowSystem : : self ( ) , & KWindowSystem : : windowAdded , this , addWindow ) ;
2017-06-29 15:50:11 -05:00
connect ( KWindowSystem : : self ( ) , & KWindowSystem : : windowRemoved , [ this ] ( WindowId wid ) noexcept {
2017-01-02 01:04:10 -05:00
if ( std : : find ( m_windows . cbegin ( ) , m_windows . cend ( ) , wid ) ! = m_windows . end ( ) ) {
m_windows . remove ( wid ) ;
emit windowRemoved ( wid ) ;
}
2016-12-28 02:57:31 -05:00
} ) ;
2017-02-24 21:40:47 -05:00
connect ( KWindowSystem : : self ( ) , & KWindowSystem : : currentDesktopChanged
2017-03-01 15:26:46 +02:00
, this , & XWindowInterface : : currentDesktopChanged ) ;
2017-02-24 21:40:47 -05:00
connect ( m_activities . data ( ) , & KActivities : : Consumer : : currentActivityChanged
2017-03-01 15:26:46 +02:00
, this , & XWindowInterface : : currentActivityChanged ) ;
2017-02-12 03:01:12 -05:00
2017-01-02 01:04:10 -05:00
// fill windows list
foreach ( const auto & wid , KWindowSystem : : self ( ) - > windows ( ) ) {
addWindow ( wid ) ;
}
2016-12-25 09:25:27 +02:00
}
XWindowInterface : : ~ XWindowInterface ( )
{
}
2017-06-15 16:56:55 -05:00
void XWindowInterface : : setDockExtraFlags ( QWindow & view )
2016-12-25 09:25:27 +02:00
{
2016-12-28 02:57:31 -05:00
NETWinInfo winfo ( QX11Info : : connection ( )
2017-02-24 21:40:47 -05:00
, static_cast < xcb_window_t > ( view . winId ( ) )
, static_cast < xcb_window_t > ( view . winId ( ) )
2016-12-28 02:57:31 -05:00
, 0 , 0 ) ;
2016-12-25 09:25:27 +02:00
2017-02-24 21:40:47 -05:00
winfo . setAllowedActions ( NET : : ActionChangeDesktop ) ;
KWindowSystem : : setType ( view . winId ( ) , NET : : Dock ) ;
KWindowSystem : : setState ( view . winId ( ) , NET : : SkipTaskbar | NET : : SkipPager ) ;
KWindowSystem : : setOnAllDesktops ( view . winId ( ) , true ) ;
2016-12-25 09:25:27 +02:00
}
2017-06-29 15:50:11 -05:00
void XWindowInterface : : setDockStruts ( QWindow & view , const QRect & rect
2017-06-15 16:56:55 -05:00
, Plasma : : Types : : Location location )
2016-12-29 00:37:53 -05:00
{
NETExtendedStrut strut ;
2017-01-16 14:07:49 -05:00
2017-06-23 16:00:16 -05:00
const auto screen = view . screen ( ) ;
2017-06-15 16:56:55 -05:00
const QRect currentScreen { screen - > geometry ( ) } ;
const QRect wholeScreen { { 0 , 0 } , screen - > virtualSize ( ) } ;
2017-03-23 00:03:45 -05:00
2016-12-29 00:37:53 -05:00
switch ( location ) {
2017-03-23 00:03:45 -05:00
case Plasma : : Types : : TopEdge : {
2017-06-15 16:56:55 -05:00
const int topOffset { screen - > geometry ( ) . top ( ) } ;
2017-06-29 15:50:11 -05:00
strut . top_width = rect . height ( ) + topOffset ;
strut . top_start = rect . x ( ) ;
strut . top_end = rect . x ( ) + rect . width ( ) - 1 ;
2016-12-29 00:37:53 -05:00
break ;
2017-03-23 00:03:45 -05:00
}
2017-01-16 14:07:49 -05:00
2017-03-23 00:03:45 -05:00
case Plasma : : Types : : BottomEdge : {
const int bottomOffset { wholeScreen . bottom ( ) - currentScreen . bottom ( ) } ;
2017-06-29 15:50:11 -05:00
strut . bottom_width = rect . height ( ) + bottomOffset ;
strut . bottom_start = rect . x ( ) ;
strut . bottom_end = rect . x ( ) + rect . width ( ) - 1 ;
2016-12-29 00:37:53 -05:00
break ;
2017-03-23 00:03:45 -05:00
}
2017-12-24 20:54:45 +02:00
2017-03-23 00:03:45 -05:00
case Plasma : : Types : : LeftEdge : {
2017-06-15 16:56:55 -05:00
const int leftOffset = { screen - > geometry ( ) . left ( ) } ;
2017-06-29 15:50:11 -05:00
strut . left_width = rect . width ( ) + leftOffset ;
strut . left_start = rect . y ( ) ;
strut . left_end = rect . y ( ) + rect . height ( ) - 1 ;
2016-12-29 00:37:53 -05:00
break ;
2017-03-23 00:03:45 -05:00
}
2017-12-24 20:54:45 +02:00
2017-03-23 00:03:45 -05:00
case Plasma : : Types : : RightEdge : {
const int rightOffset = { wholeScreen . right ( ) - currentScreen . right ( ) } ;
2017-06-29 15:50:11 -05:00
strut . right_width = rect . width ( ) + rightOffset ;
strut . right_start = rect . y ( ) ;
strut . right_end = rect . y ( ) + rect . height ( ) - 1 ;
2016-12-29 00:37:53 -05:00
break ;
2017-03-23 00:03:45 -05:00
}
2017-12-24 20:54:45 +02:00
2016-12-29 00:37:53 -05:00
default :
qWarning ( ) < < " wrong location: " < < qEnumToStr ( location ) ;
return ;
}
2017-01-16 14:07:49 -05:00
2017-06-15 16:56:55 -05:00
KWindowSystem : : setExtendedStrut ( view . winId ( ) ,
2016-12-29 00:37:53 -05:00
strut . left_width , strut . left_start , strut . left_end ,
strut . right_width , strut . right_start , strut . right_end ,
strut . top_width , strut . top_start , strut . top_end ,
strut . bottom_width , strut . bottom_start , strut . bottom_end
) ;
}
2018-01-21 11:53:15 +02:00
void XWindowInterface : : setWindowOnActivities ( QWindow & window , const QStringList & activities )
2018-01-13 12:55:13 +02:00
{
2018-01-21 11:53:15 +02:00
KWindowSystem : : setOnActivities ( window . winId ( ) , activities ) ;
2018-01-13 12:55:13 +02:00
}
2017-06-15 16:56:55 -05:00
void XWindowInterface : : removeDockStruts ( QWindow & view ) const
2017-02-24 21:40:47 -05:00
{
2017-06-15 16:56:55 -05:00
KWindowSystem : : setStrut ( view . winId ( ) , 0 , 0 , 0 , 0 ) ;
2017-02-24 21:40:47 -05:00
}
2017-06-08 17:10:49 -05:00
WindowId XWindowInterface : : activeWindow ( ) const
2017-02-24 21:40:47 -05:00
{
return KWindowSystem : : self ( ) - > activeWindow ( ) ;
}
2017-06-08 17:10:49 -05:00
const std : : list < WindowId > & XWindowInterface : : windows ( ) const
2017-02-24 21:40:47 -05:00
{
return m_windows ;
}
2018-01-26 19:46:02 +02:00
void XWindowInterface : : setKeepAbove ( const QDialog & dialog , bool above ) const
{
if ( above ) {
KWindowSystem : : setState ( dialog . winId ( ) , NET : : KeepAbove ) ;
} else {
KWindowSystem : : clearState ( dialog . winId ( ) , NET : : KeepAbove ) ;
}
}
2017-02-24 21:40:47 -05:00
void XWindowInterface : : skipTaskBar ( const QDialog & dialog ) const
{
KWindowSystem : : setState ( dialog . winId ( ) , NET : : SkipTaskbar ) ;
}
2017-06-15 16:56:55 -05:00
void XWindowInterface : : slideWindow ( QWindow & view , AbstractWindowInterface : : Slide location ) const
2017-02-24 21:40:47 -05:00
{
auto slideLocation = KWindowEffects : : NoEdge ;
switch ( location ) {
case Slide : : Top :
slideLocation = KWindowEffects : : TopEdge ;
break ;
case Slide : : Bottom :
slideLocation = KWindowEffects : : BottomEdge ;
break ;
case Slide : : Left :
slideLocation = KWindowEffects : : LeftEdge ;
break ;
case Slide : : Right :
slideLocation = KWindowEffects : : RightEdge ;
break ;
default :
2017-03-01 15:26:46 +02:00
break ;
2017-02-24 21:40:47 -05:00
}
KWindowEffects : : slideWindow ( view . winId ( ) , slideLocation , - 1 ) ;
}
2017-06-15 16:56:55 -05:00
void XWindowInterface : : enableBlurBehind ( QWindow & view ) const
2016-12-29 00:37:53 -05:00
{
2017-02-24 21:40:47 -05:00
KWindowEffects : : enableBlurBehind ( view . winId ( ) ) ;
2016-12-29 00:37:53 -05:00
}
2017-02-24 21:40:47 -05:00
2018-03-28 20:39:52 +03:00
void XWindowInterface : : setEdgeStateFor ( QWindow * view , bool active ) const
{
ScreenEdgeGhostWindow * window = qobject_cast < ScreenEdgeGhostWindow * > ( view ) ;
if ( ! window ) {
return ;
}
xcb_connection_t * c = QX11Info : : connection ( ) ;
const QByteArray effectName = QByteArrayLiteral ( " _KDE_NET_WM_SCREEN_EDGE_SHOW " ) ;
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked ( c , false , effectName . length ( ) , effectName . constData ( ) ) ;
QScopedPointer < xcb_intern_atom_reply_t , QScopedPointerPodDeleter > atom ( xcb_intern_atom_reply ( c , atomCookie , nullptr ) ) ;
if ( ! atom ) {
return ;
}
if ( ! active ) {
xcb_delete_property ( c , window - > winId ( ) , atom - > atom ) ;
window - > hideWithMask ( ) ;
return ;
}
window - > showWithMask ( ) ;
uint32_t value = 0 ;
switch ( window - > location ( ) ) {
case Plasma : : Types : : TopEdge :
value = 0 ;
break ;
case Plasma : : Types : : RightEdge :
value = 1 ;
break ;
case Plasma : : Types : : BottomEdge :
value = 2 ;
break ;
case Plasma : : Types : : LeftEdge :
value = 3 ;
break ;
case Plasma : : Types : : Floating :
default :
value = 4 ;
break ;
}
int hideType = 0 ;
value | = hideType < < 8 ;
xcb_change_property ( c , XCB_PROP_MODE_REPLACE , window - > winId ( ) , atom - > atom , XCB_ATOM_CARDINAL , 32 , 1 , & value ) ;
}
2017-01-02 01:04:10 -05:00
WindowInfoWrap XWindowInterface : : requestInfoActive ( ) const
2016-12-25 09:25:27 +02:00
{
2016-12-28 02:57:31 -05:00
return requestInfo ( KWindowSystem : : activeWindow ( ) ) ;
2016-12-25 09:25:27 +02:00
}
2017-06-08 17:10:49 -05:00
bool XWindowInterface : : isOnCurrentDesktop ( WindowId wid ) const
2017-01-02 01:04:10 -05:00
{
2017-06-08 17:10:49 -05:00
KWindowInfo winfo ( wid . value < WId > ( ) , NET : : WMDesktop ) ;
2017-02-13 17:52:41 -05:00
return winfo . valid ( ) & & winfo . isOnCurrentDesktop ( ) ;
2017-01-02 01:04:10 -05:00
}
2017-06-20 19:06:04 -05:00
bool XWindowInterface : : isOnCurrentActivity ( WindowId wid ) const
{
KWindowInfo winfo ( wid . value < WId > ( ) , 0 , NET : : WM2Activities ) ;
return winfo . valid ( )
2017-12-24 20:54:45 +02:00
& & ( winfo . activities ( ) . contains ( m_activities - > currentActivity ( ) ) | | winfo . activities ( ) . empty ( ) ) ;
2017-06-20 19:06:04 -05:00
}
2017-06-08 17:10:49 -05:00
WindowInfoWrap XWindowInterface : : requestInfo ( WindowId wid ) const
2016-12-25 09:25:27 +02:00
{
2017-06-08 17:10:49 -05:00
const KWindowInfo winfo { wid . value < WId > ( ) , NET : : WMFrameExtents
2018-07-03 22:15:45 +03:00
| NET : : WMWindowType
| NET : : WMGeometry
| NET : : WMState } ;
2017-03-15 02:57:42 -05:00
2016-12-28 02:57:31 -05:00
WindowInfoWrap winfoWrap ;
2017-01-16 14:07:49 -05:00
2017-03-15 02:57:42 -05:00
if ( isValidWindow ( winfo ) ) {
2017-01-02 01:04:10 -05:00
winfoWrap . setIsValid ( true ) ;
winfoWrap . setWid ( wid ) ;
2017-06-08 17:10:49 -05:00
winfoWrap . setIsActive ( KWindowSystem : : activeWindow ( ) = = wid . value < WId > ( ) ) ;
2017-01-02 01:04:10 -05:00
winfoWrap . setIsMinimized ( winfo . hasState ( NET : : Hidden ) ) ;
2017-03-12 04:01:27 -05:00
winfoWrap . setIsMaxVert ( winfo . hasState ( NET : : MaxVert ) ) ;
winfoWrap . setIsMaxHoriz ( winfo . hasState ( NET : : MaxHoriz ) ) ;
2017-01-02 01:04:10 -05:00
winfoWrap . setIsFullscreen ( winfo . hasState ( NET : : FullScreen ) ) ;
2017-03-23 22:06:59 -05:00
winfoWrap . setIsShaded ( winfo . hasState ( NET : : Shaded ) ) ;
2017-02-11 01:58:25 -05:00
winfoWrap . setGeometry ( winfo . frameGeometry ( ) ) ;
2017-12-24 20:54:45 +02:00
winfoWrap . setIsKeepAbove ( winfo . hasState ( NET : : KeepAbove ) ) ;
2017-01-02 01:04:10 -05:00
} else if ( m_desktopId = = wid ) {
winfoWrap . setIsValid ( true ) ;
winfoWrap . setIsPlasmaDesktop ( true ) ;
winfoWrap . setWid ( wid ) ;
}
2017-01-16 14:07:49 -05:00
2016-12-28 02:57:31 -05:00
return winfoWrap ;
}
2017-01-02 01:04:10 -05:00
bool XWindowInterface : : isValidWindow ( const KWindowInfo & winfo ) const
2016-12-28 02:57:31 -05:00
{
2017-03-15 02:57:42 -05:00
constexpr auto types = NET : : DockMask | NET : : MenuMask | NET : : SplashMask | NET : : NormalMask ;
auto winType = winfo . windowType ( types ) ;
2017-01-16 14:07:49 -05:00
2017-01-24 14:27:57 -05:00
if ( winType = = - 1 ) {
2017-03-15 02:57:42 -05:00
// Trying to get more types for verify if the window have any other type
winType = winfo . windowType ( ~ types & NET : : AllTypesMask ) ;
2017-01-16 14:07:49 -05:00
2017-03-15 02:57:42 -05:00
if ( winType = = - 1 ) {
qWarning ( ) < < KWindowInfo ( winfo . win ( ) , 0 , NET : : WM2WindowClass ) . windowClassName ( )
< < " doesn't have any WindowType, assuming as NET::Normal " ;
2017-01-24 14:27:57 -05:00
return true ;
2017-03-15 02:57:42 -05:00
}
2017-01-24 14:27:57 -05:00
}
return ! ( ( winType & NET : : Menu ) | | ( winType & NET : : Dock ) | | ( winType & NET : : Splash ) ) ;
2016-12-25 09:25:27 +02:00
}
2016-12-28 02:57:31 -05:00
void XWindowInterface : : windowChangedProxy ( WId wid , NET : : Properties prop1 , NET : : Properties2 prop2 )
2016-12-25 09:25:27 +02:00
{
2016-12-28 02:57:31 -05:00
//! if the dock changed is ignored
2017-02-24 21:40:47 -05:00
if ( std : : find ( m_docks . cbegin ( ) , m_docks . cend ( ) , wid ) ! = m_docks . cend ( ) )
2016-12-28 02:57:31 -05:00
return ;
2017-01-16 14:07:49 -05:00
2017-01-02 01:04:10 -05:00
const auto winType = KWindowInfo ( wid , NET : : WMWindowType ) . windowType ( NET : : DesktopMask ) ;
2017-01-16 14:07:49 -05:00
2018-10-06 16:28:46 +03:00
//! update desktop id
2017-01-02 01:04:10 -05:00
if ( winType ! = - 1 & & ( winType & NET : : Desktop ) ) {
m_desktopId = wid ;
emit windowChanged ( wid ) ;
2016-12-28 02:57:31 -05:00
return ;
2017-01-02 01:04:10 -05:00
}
2017-01-16 14:07:49 -05:00
2018-10-06 16:28:46 +03:00
//! accept only NET::Properties events,
//! ignore when the user presses a key, or a window is sending X events etc.
2018-01-02 14:03:50 +02:00
//! without needing to (e.g. Firefox, https://bugzilla.mozilla.org/show_bug.cgi?id=1389953)
2018-10-06 16:28:46 +03:00
//! NET::WM2UserTime, NET::WM2IconPixmap etc....
if ( prop1 = = 0 ) {
2017-01-02 01:04:10 -05:00
return ;
}
2017-01-16 14:07:49 -05:00
2018-10-06 16:28:46 +03:00
//! accepty only the following NET:Properties changed signals
//! NET::WMState, NET::WMGeometry, NET::ActiveWindow
if ( ! ( ( prop1 & NET : : WMState ) | | ( prop1 & NET : : WMGeometry ) | | ( prop1 & NET : : ActiveWindow ) ) ) {
2016-12-28 02:57:31 -05:00
return ;
2018-10-06 16:28:46 +03:00
}
//! when only WMState changed we can whitelist the acceptable states
if ( ( prop1 & NET : : WMState ) & & ! ( prop1 & NET : : WMGeometry ) & & ! ( prop1 & NET : : ActiveWindow ) ) {
KWindowInfo info ( wid , NET : : WMState ) ;
if ( info . valid ( ) ) {
if ( ! info . hasState ( NET : : Sticky ) & & ! info . hasState ( NET : : Shaded )
& & ! info . hasState ( NET : : FullScreen ) & & ! info . hasState ( NET : : Hidden ) ) {
return ;
}
} else {
return ;
}
}
2017-01-16 14:07:49 -05:00
2016-12-30 02:11:42 -05:00
emit windowChanged ( wid ) ;
2016-12-25 09:25:27 +02:00
}
}