2017-04-25 17:48:36 +03: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/>.
*/
# include "waylandinterface.h"
# include "../liblattedock/extras.h"
# include <QDebug>
# include <QTimer>
2017-06-10 16:33:46 -05:00
# include <QApplication>
# include <QSignalMapper>
2017-04-25 17:48:36 +03:00
# include <QtX11Extras/QX11Info>
2017-06-29 15:33:12 -05:00
# include <QRasterWindow>
2017-04-25 17:48:36 +03:00
# include <KWindowSystem>
# include <KWindowInfo>
# include <NETWM>
2017-06-29 15:33:12 -05:00
2017-06-10 16:33:46 -05:00
using namespace KWayland : : Client ;
2017-04-25 17:48:36 +03:00
namespace Latte {
2017-06-29 15:33:12 -05:00
class Private : : GhostWindow : public QRasterWindow {
Q_OBJECT
public :
GhostWindow ( WaylandInterface * waylandInterface )
: m_waylandInterface ( waylandInterface )
{
setFlags ( Qt : : FramelessWindowHint
| Qt : : WindowStaysOnTopHint
| Qt : : NoDropShadowWindowHint
| Qt : : WindowDoesNotAcceptFocus ) ;
setupWaylandIntegration ( ) ;
show ( ) ;
}
~ GhostWindow ( )
{
delete m_shellSurface ;
}
void setGeometry ( const QRect & rect )
{
QWindow : : setGeometry ( rect ) ;
m_shellSurface - > setPosition ( rect . topLeft ( ) ) ;
}
void setupWaylandIntegration ( )
{
using namespace KWayland : : Client ;
if ( m_shellSurface )
return ;
Surface * s { Surface : : fromWindow ( this ) } ;
if ( ! s )
return ;
m_shellSurface = m_waylandInterface - > m_plasmaShell - > createSurface ( s , this ) ;
qDebug ( ) < < " wayland ghost window surface was created... " ;
m_shellSurface - > setSkipTaskbar ( true ) ;
m_shellSurface - > setPanelTakesFocus ( false ) ;
m_shellSurface - > setRole ( PlasmaShellSurface : : Role : : Panel ) ;
m_shellSurface - > setPanelBehavior ( PlasmaShellSurface : : PanelBehavior : : AlwaysVisible ) ;
}
KWayland : : Client : : PlasmaShellSurface * m_shellSurface { nullptr } ;
WaylandInterface * m_waylandInterface { nullptr } ;
} ;
2017-04-25 17:48:36 +03:00
WaylandInterface : : WaylandInterface ( QObject * parent )
: AbstractWindowInterface ( parent )
{
m_activities = new KActivities : : Consumer ( this ) ;
2017-06-10 16:33:46 -05:00
m_connection = ConnectionThread : : fromApplication ( this ) ;
if ( ! m_connection ) {
qWarning ( ) < < " Failed getting Wayland connection from QPA " ;
return ;
}
m_registry = new Registry ( this ) ;
m_registry - > create ( m_connection ) ;
2017-06-15 16:56:55 -05:00
connect ( qApp , & QCoreApplication : : aboutToQuit , this , [ & ] ( ) {
2017-06-10 16:33:46 -05:00
if ( m_wm )
m_wm - > release ( ) ;
if ( m_plasmaShell )
m_plasmaShell - > release ( ) ;
m_registry - > release ( ) ;
2017-04-25 17:48:36 +03:00
} ) ;
2017-06-10 16:33:46 -05:00
m_registry - > setup ( ) ;
m_connection - > roundtrip ( ) ;
2017-06-23 16:00:16 -05:00
const auto wmInterface = m_registry - > interface ( Registry : : Interface : : PlasmaWindowManagement ) ;
2017-06-10 16:33:46 -05:00
if ( wmInterface . name = = 0 ) {
qWarning ( ) < < " This compositor does not support the Plasma Window Management interface " ;
return ;
}
m_wm = m_registry - > createPlasmaWindowManagement ( wmInterface . name , wmInterface . version , this ) ;
connect ( m_wm , & PlasmaWindowManagement : : windowCreated , this , & WaylandInterface : : windowCreatedProxy ) ;
2017-06-29 15:50:11 -05:00
connect ( m_wm , & PlasmaWindowManagement : : activeWindowChanged , this , [ & ] ( ) noexcept {
2017-06-23 16:10:14 -05:00
auto w = m_wm - > activeWindow ( ) ;
2017-06-14 16:10:31 -05:00
emit activeWindowChanged ( w ? w - > internalId ( ) : 0 ) ;
2017-06-14 12:50:17 -05:00
} , Qt : : QueuedConnection ) ;
2017-06-10 16:33:46 -05:00
2017-06-23 16:00:16 -05:00
const auto shellInterface = m_registry - > interface ( Registry : : Interface : : PlasmaShell ) ;
2017-06-10 16:33:46 -05:00
if ( shellInterface . name = = 0 ) {
qWarning ( ) < < " Plasma Shell interface can't be created " ;
return ;
}
m_plasmaShell = m_registry - > createPlasmaShell ( shellInterface . name , shellInterface . version , this ) ;
2017-04-25 17:48:36 +03:00
connect ( m_activities . data ( ) , & KActivities : : Consumer : : currentActivityChanged
, this , & WaylandInterface : : currentActivityChanged ) ;
}
WaylandInterface : : ~ WaylandInterface ( )
{
}
2017-06-10 16:33:46 -05:00
void WaylandInterface : : init ( )
{
}
2017-06-15 16:56:55 -05:00
void WaylandInterface : : setDockExtraFlags ( QWindow & view )
2017-04-25 17:48:36 +03:00
{
2017-06-15 16:56:55 -05:00
Q_UNUSED ( view )
2017-04-25 17:48:36 +03:00
}
2017-06-29 15:33:12 -05:00
void WaylandInterface : : setDockStruts ( QWindow & view , const QRect & rect , Plasma : : Types : : Location location )
2017-04-25 17:48:36 +03:00
{
2017-06-29 15:33:12 -05:00
if ( ! m_ghostWindows . contains ( view . winId ( ) ) )
m_ghostWindows [ view . winId ( ) ] = new Private : : GhostWindow ( this ) ;
auto w = m_ghostWindows [ view . winId ( ) ] ;
switch ( location ) {
case Plasma : : Types : : TopEdge :
case Plasma : : Types : : BottomEdge :
w - > setGeometry ( { rect . x ( ) + rect . width ( ) / 2 , rect . y ( ) , 1 , rect . height ( ) } ) ;
break ;
case Plasma : : Types : : LeftEdge :
case Plasma : : Types : : RightEdge :
w - > setGeometry ( { rect . x ( ) , rect . y ( ) + rect . height ( ) / 2 , rect . width ( ) , 1 } ) ;
break ;
default :
break ;
}
2017-04-25 17:48:36 +03:00
}
2017-06-15 16:56:55 -05:00
void WaylandInterface : : removeDockStruts ( QWindow & view ) const
2017-04-25 17:48:36 +03:00
{
2017-06-29 15:33:12 -05:00
delete m_ghostWindows . take ( view . winId ( ) ) ;
2017-04-25 17:48:36 +03:00
}
2017-06-08 17:10:49 -05:00
WindowId WaylandInterface : : activeWindow ( ) const
2017-04-25 17:48:36 +03:00
{
2017-06-23 16:11:43 -05:00
auto wid = m_wm - > activeWindow ( ) ;
2017-06-10 16:33:46 -05:00
return wid ? wid - > internalId ( ) : 0 ;
2017-04-25 17:48:36 +03:00
}
2017-06-08 17:10:49 -05:00
const std : : list < WindowId > & WaylandInterface : : windows ( ) const
2017-04-25 17:48:36 +03:00
{
return m_windows ;
}
void WaylandInterface : : skipTaskBar ( const QDialog & dialog ) const
{
KWindowSystem : : setState ( dialog . winId ( ) , NET : : SkipTaskbar ) ;
}
2017-06-15 16:56:55 -05:00
void WaylandInterface : : slideWindow ( QWindow & view , AbstractWindowInterface : : Slide location ) const
2017-04-25 17:48:36 +03: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 :
break ;
}
KWindowEffects : : slideWindow ( view . winId ( ) , slideLocation , - 1 ) ;
}
2017-06-15 16:56:55 -05:00
void WaylandInterface : : enableBlurBehind ( QWindow & view ) const
2017-04-25 17:48:36 +03:00
{
KWindowEffects : : enableBlurBehind ( view . winId ( ) ) ;
}
WindowInfoWrap WaylandInterface : : requestInfoActive ( ) const
{
2017-06-23 16:00:16 -05:00
auto w = m_wm - > activeWindow ( ) ;
2017-06-10 16:33:46 -05:00
if ( ! w ) return { } ;
WindowInfoWrap winfoWrap ;
winfoWrap . setIsValid ( true ) ;
winfoWrap . setWid ( w - > internalId ( ) ) ;
winfoWrap . setIsActive ( w - > isActive ( ) ) ;
2017-06-14 12:50:17 -05:00
winfoWrap . setIsMinimized ( w - > isMinimized ( ) ) ;
2017-06-10 16:33:46 -05:00
winfoWrap . setIsMaxVert ( w - > isMaximized ( ) ) ;
winfoWrap . setIsMaxHoriz ( w - > isMaximized ( ) ) ;
winfoWrap . setIsFullscreen ( w - > isFullscreen ( ) ) ;
winfoWrap . setIsShaded ( w - > isShaded ( ) ) ;
2017-06-14 12:50:17 -05:00
winfoWrap . setGeometry ( w - > geometry ( ) ) ;
2017-06-10 16:33:46 -05:00
return winfoWrap ;
2017-04-25 17:48:36 +03:00
}
2017-06-08 17:10:49 -05:00
bool WaylandInterface : : isOnCurrentDesktop ( WindowId wid ) const
2017-04-25 17:48:36 +03:00
{
2017-06-29 15:50:11 -05:00
auto it = std : : find_if ( m_wm - > windows ( ) . constBegin ( ) , m_wm - > windows ( ) . constEnd ( ) , [ & wid ] ( PlasmaWindow * w ) noexcept {
2017-06-10 16:33:46 -05:00
return w - > isValid ( ) & & w - > internalId ( ) = = wid ;
} ) ;
2017-06-14 14:45:43 -05:00
//qDebug() << "desktop:" << (it != m_wm->windows().constEnd() ? (*it)->virtualDesktop() : -1) << KWindowSystem::currentDesktop();
//return true;
2017-06-10 16:33:46 -05:00
return it ! = m_wm - > windows ( ) . constEnd ( ) & & ( ( * it ) - > virtualDesktop ( ) = = KWindowSystem : : currentDesktop ( ) | | ( * it ) - > isOnAllDesktops ( ) ) ;
2017-04-25 17:48:36 +03:00
}
2017-06-20 19:07:47 -05:00
bool WaylandInterface : : isOnCurrentActivity ( WindowId wid ) const
{
2017-06-29 15:50:11 -05:00
auto it = std : : find_if ( m_wm - > windows ( ) . constBegin ( ) , m_wm - > windows ( ) . constEnd ( ) , [ & wid ] ( PlasmaWindow * w ) noexcept {
2017-06-20 19:07:47 -05:00
return w - > isValid ( ) & & w - > internalId ( ) = = wid ;
} ) ;
//TODO: Not yet implemented
return it ! = m_wm - > windows ( ) . constEnd ( ) & & true ;
}
2017-06-08 17:10:49 -05:00
WindowInfoWrap WaylandInterface : : requestInfo ( WindowId wid ) const
2017-04-25 17:48:36 +03:00
{
2017-06-29 17:02:25 -05:00
auto it = std : : find_if ( m_wm - > windows ( ) . constBegin ( ) , m_wm - > windows ( ) . constEnd ( ) , [ & wid ] ( PlasmaWindow * w ) noexcept {
2017-06-10 16:33:46 -05:00
return w - > isValid ( ) & & w - > internalId ( ) = = wid ;
} ) ;
2017-04-25 17:48:36 +03:00
2017-06-29 17:02:25 -05:00
if ( it = = m_wm - > windows ( ) . constEnd ( ) )
return { } ;
WindowInfoWrap winfoWrap ;
auto w = * it ;
if ( isValidWindow ( w ) ) {
2017-06-10 16:33:46 -05:00
winfoWrap . setIsValid ( true ) ;
winfoWrap . setWid ( wid ) ;
winfoWrap . setIsActive ( w - > isActive ( ) ) ;
2017-06-14 12:50:17 -05:00
winfoWrap . setIsMinimized ( w - > isMinimized ( ) ) ;
2017-06-10 16:33:46 -05:00
winfoWrap . setIsMaxVert ( w - > isMaximized ( ) ) ;
winfoWrap . setIsMaxHoriz ( w - > isMaximized ( ) ) ;
winfoWrap . setIsFullscreen ( w - > isFullscreen ( ) ) ;
winfoWrap . setIsShaded ( w - > isShaded ( ) ) ;
2017-06-14 12:50:17 -05:00
winfoWrap . setGeometry ( w - > geometry ( ) ) ;
2017-06-29 17:02:25 -05:00
} else if ( w - > appId ( ) = = QLatin1String ( " org.kde.plasmashell " ) ) {
winfoWrap . setIsValid ( true ) ;
winfoWrap . setIsPlasmaDesktop ( true ) ;
2017-06-10 16:33:46 -05:00
winfoWrap . setWid ( wid ) ;
}
2017-04-25 17:48:36 +03:00
return winfoWrap ;
}
2017-06-10 16:33:46 -05:00
inline bool WaylandInterface : : isValidWindow ( const KWayland : : Client : : PlasmaWindow * w ) const
2017-04-25 17:48:36 +03:00
{
2017-06-10 16:33:46 -05:00
return w - > isValid ( ) & & ! w - > skipTaskbar ( ) ;
2017-04-25 17:48:36 +03:00
}
2017-06-10 16:33:46 -05:00
void WaylandInterface : : windowCreatedProxy ( KWayland : : Client : : PlasmaWindow * w )
2017-04-25 17:48:36 +03:00
{
2017-06-10 16:33:46 -05:00
if ( ! isValidWindow ( w ) ) return ;
2017-04-25 17:48:36 +03:00
2017-06-10 16:33:46 -05:00
if ( ! mapper ) mapper = new QSignalMapper ( this ) ;
2017-04-25 17:48:36 +03:00
2017-06-10 16:33:46 -05:00
mapper - > setMapping ( w , w ) ;
2017-04-25 17:48:36 +03:00
2017-06-29 15:50:11 -05:00
connect ( w , & PlasmaWindow : : unmapped , this , [ & , win = w ] ( ) noexcept {
2017-06-10 16:33:46 -05:00
mapper - > removeMappings ( win ) ;
m_windows . remove ( win - > internalId ( ) ) ;
emit windowRemoved ( win - > internalId ( ) ) ;
} ) ;
2017-06-15 16:56:55 -05:00
connect ( w , SIGNAL ( activeChanged ( ) ) , mapper , SLOT ( map ( ) ) ) ;
2017-06-10 16:33:46 -05:00
connect ( w , SIGNAL ( fullscreenChanged ( ) ) , mapper , SLOT ( map ( ) ) ) ;
connect ( w , SIGNAL ( geometryChanged ( ) ) , mapper , SLOT ( map ( ) ) ) ;
connect ( w , SIGNAL ( maximizedChanged ( ) ) , mapper , SLOT ( map ( ) ) ) ;
2017-06-14 14:45:43 -05:00
connect ( w , SIGNAL ( minimizedChanged ( ) ) , mapper , SLOT ( map ( ) ) ) ;
2017-06-10 16:33:46 -05:00
connect ( w , SIGNAL ( shadedChanged ( ) ) , mapper , SLOT ( map ( ) ) ) ;
2017-06-14 16:10:31 -05:00
connect ( w , SIGNAL ( skipTaskbarChanged ( ) ) , mapper , SLOT ( map ( ) ) ) ;
2017-06-14 14:45:43 -05:00
connect ( w , SIGNAL ( onAllDesktopsChanged ( ) ) , mapper , SLOT ( map ( ) ) ) ;
2017-06-10 16:33:46 -05:00
connect ( w , SIGNAL ( virtualDesktopChanged ( ) ) , mapper , SLOT ( map ( ) ) ) ;
connect ( mapper , static_cast < void ( QSignalMapper : : * ) ( QObject * ) > ( & QSignalMapper : : mapped )
2017-06-29 15:50:11 -05:00
, this , [ & ] ( QObject * w ) noexcept
2017-06-10 16:33:46 -05:00
{
2017-06-14 14:45:43 -05:00
qDebug ( ) < < " window changed: " < < qobject_cast < PlasmaWindow * > ( w ) - > appId ( ) ;
2017-06-10 16:33:46 -05:00
emit windowChanged ( qobject_cast < PlasmaWindow * > ( w ) - > internalId ( ) ) ;
} ) ;
2017-04-25 17:48:36 +03:00
2017-06-10 16:33:46 -05:00
m_windows . push_back ( w - > internalId ( ) ) ;
2017-04-25 17:48:36 +03:00
2017-06-10 16:33:46 -05:00
emit windowAdded ( w - > internalId ( ) ) ;
2017-04-25 17:48:36 +03:00
}
}
2017-06-29 15:34:36 -05:00
# include "waylandinterface.moc"