2017-01-02 17:05:30 -05:00
/*
2021-05-27 15:01:00 +00:00
SPDX - FileCopyrightText : 2016 Smith AR < audoban @ openmailbox . org >
SPDX - FileCopyrightText : 2016 Michail Vourlakos < mvourlakos @ gmail . com >
SPDX - License - Identifier : GPL - 2.0 - or - later
2017-01-02 17:05:30 -05:00
*/
2016-12-25 09:25:27 +02:00
# include "xwindowinterface.h"
2018-12-02 02:05:52 +02:00
// local
2020-04-24 14:52:16 +03:00
# include <coretypes.h>
2019-06-01 01:20:54 +03:00
# include "tasktools.h"
2018-12-06 12:15:58 +02:00
# include "view/view.h"
2020-01-22 19:45:13 +02:00
# include "view/helpers/screenedgeghostwindow.h"
2016-12-25 09:25:27 +02:00
2018-12-02 02:05:52 +02:00
// Qt
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
2018-12-02 02:05:52 +02:00
// KDE
2019-06-01 01:20:54 +03:00
# include <KDesktopFile>
2016-12-25 09:25:27 +02:00
# include <KWindowSystem>
2016-12-30 02:11:42 -05:00
# include <KWindowInfo>
2019-05-27 18:56:24 +03:00
# include <KIconThemes/KIconLoader>
2016-12-25 09:25:27 +02:00
2018-12-02 02:05:52 +02:00
// X11
# include <NETWM>
2018-03-28 20:39:52 +03:00
# include <xcb/xcb.h>
2020-07-19 15:04:46 +03:00
# include <xcb/shape.h>
2018-03-28 20:39:52 +03:00
2016-12-28 02:57:31 -05:00
namespace Latte {
2019-05-11 15:43:10 +03:00
namespace WindowSystem {
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
{
2019-06-08 00:31:30 +03:00
m_currentDesktop = QString ( KWindowSystem : : self ( ) - > currentDesktop ( ) ) ;
2019-06-01 01:20:54 +03:00
2019-05-12 02:35:28 +03:00
connect ( KWindowSystem : : self ( ) , & KWindowSystem : : activeWindowChanged , this , & AbstractWindowInterface : : activeWindowChanged ) ;
connect ( KWindowSystem : : self ( ) , & KWindowSystem : : windowRemoved , this , & AbstractWindowInterface : : windowRemoved ) ;
2019-06-08 00:31:30 +03:00
2020-03-05 21:20:25 +02:00
connect ( KWindowSystem : : self ( ) , & KWindowSystem : : windowAdded , this , & XWindowInterface : : windowAddedProxy ) ;
2019-06-08 00:31:30 +03:00
connect ( KWindowSystem : : self ( ) , & KWindowSystem : : currentDesktopChanged , this , [ & ] ( int desktop ) {
m_currentDesktop = QString ( desktop ) ;
emit currentDesktopChanged ( ) ;
} ) ;
2019-05-12 02:35:28 +03:00
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 ) ;
2019-11-02 10:45:37 +02:00
for ( auto wid : KWindowSystem : : self ( ) - > windows ( ) ) {
2020-03-05 21:20:25 +02:00
windowAddedProxy ( wid ) ;
2019-11-02 10:45:37 +02:00
}
2016-12-25 09:25:27 +02:00
}
XWindowInterface : : ~ XWindowInterface ( )
{
}
2019-12-26 16:53:37 +02:00
void XWindowInterface : : setViewExtraFlags ( QObject * view , bool isPanelWindow , Latte : : Types : : Visibility mode )
2016-12-25 09:25:27 +02:00
{
2019-12-26 16:53:37 +02:00
WId winId = - 1 ;
QQuickView * quickView = qobject_cast < QQuickView * > ( view ) ;
if ( quickView ) {
winId = quickView - > winId ( ) ;
}
if ( ! quickView ) {
QQuickWindow * quickWindow = qobject_cast < QQuickWindow * > ( view ) ;
if ( quickWindow ) {
winId = quickWindow - > winId ( ) ;
}
}
2016-12-28 02:57:31 -05:00
NETWinInfo winfo ( QX11Info : : connection ( )
2019-12-26 16:53:37 +02:00
, static_cast < xcb_window_t > ( winId )
, static_cast < xcb_window_t > ( 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 ) ;
2019-12-26 16:53:37 +02:00
if ( isPanelWindow ) {
KWindowSystem : : setType ( winId , NET : : Dock ) ;
2019-12-26 21:10:11 +02:00
} else {
KWindowSystem : : setType ( winId , NET : : Normal ) ;
2019-12-26 16:53:37 +02:00
}
2019-12-26 21:58:21 +02:00
# if KF5_VERSION_MINOR >= 45
KWindowSystem : : setState ( winId , NET : : SkipTaskbar | NET : : SkipPager | NET : : SkipSwitcher ) ;
# else
2019-12-26 16:53:37 +02:00
KWindowSystem : : setState ( winId , NET : : SkipTaskbar | NET : : SkipPager ) ;
2019-12-26 21:58:21 +02:00
# endif
2019-12-26 16:53:37 +02:00
KWindowSystem : : setOnAllDesktops ( winId , true ) ;
2019-12-26 21:10:11 +02:00
2019-12-26 21:58:21 +02:00
//! Layer to be applied
2019-12-26 21:10:11 +02:00
if ( mode = = Latte : : Types : : WindowsCanCover | | mode = = Latte : : Types : : WindowsAlwaysCover ) {
setKeepBelow ( winId , true ) ;
2019-12-26 21:58:21 +02:00
} else if ( mode = = Latte : : Types : : NormalWindow ) {
setKeepBelow ( winId , false ) ;
setKeepAbove ( winId , false ) ;
2019-12-26 21:10:11 +02:00
} else {
setKeepAbove ( winId , true ) ;
}
2016-12-25 09:25:27 +02:00
}
2018-12-06 16:09:42 +02:00
void XWindowInterface : : setViewStruts ( 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 ) {
2019-11-02 08:47:31 +02:00
case Plasma : : Types : : TopEdge : {
const int topOffset { screen - > geometry ( ) . top ( ) } ;
strut . top_width = rect . height ( ) + topOffset ;
strut . top_start = rect . x ( ) ;
strut . top_end = rect . x ( ) + rect . width ( ) - 1 ;
break ;
}
2017-01-16 14:07:49 -05:00
2019-11-02 08:47:31 +02:00
case Plasma : : Types : : BottomEdge : {
const int bottomOffset { wholeScreen . bottom ( ) - currentScreen . bottom ( ) } ;
strut . bottom_width = rect . height ( ) + bottomOffset ;
strut . bottom_start = rect . x ( ) ;
strut . bottom_end = rect . x ( ) + rect . width ( ) - 1 ;
break ;
}
2017-12-24 20:54:45 +02:00
2019-11-02 08:47:31 +02:00
case Plasma : : Types : : LeftEdge : {
const int leftOffset = { screen - > geometry ( ) . left ( ) } ;
strut . left_width = rect . width ( ) + leftOffset ;
strut . left_start = rect . y ( ) ;
strut . left_end = rect . y ( ) + rect . height ( ) - 1 ;
break ;
}
2017-12-24 20:54:45 +02:00
2019-11-02 08:47:31 +02:00
case Plasma : : Types : : RightEdge : {
const int rightOffset = { wholeScreen . right ( ) - currentScreen . right ( ) } ;
strut . right_width = rect . width ( ) + rightOffset ;
strut . right_start = rect . y ( ) ;
strut . right_end = rect . y ( ) + rect . height ( ) - 1 ;
break ;
}
2017-12-24 20:54:45 +02:00
2019-11-02 08:47:31 +02:00
default :
2020-04-18 13:34:59 +03:00
qWarning ( ) < < " wrong location: " < < location ;
2019-11-02 08:47:31 +02:00
return ;
2016-12-29 00:37:53 -05:00
}
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
2019-11-02 08:47:31 +02:00
) ;
2016-12-29 00:37:53 -05:00
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : switchToNextVirtualDesktop ( )
2019-06-10 17:30:25 +03:00
{
int desktops = KWindowSystem : : numberOfDesktops ( ) ;
2019-06-10 17:49:21 +03:00
2019-06-10 17:30:25 +03:00
if ( desktops < = 1 ) {
return ;
}
int curPos = KWindowSystem : : currentDesktop ( ) ;
int nextPos = curPos + 1 ;
2019-06-10 17:49:21 +03:00
if ( curPos = = desktops ) {
nextPos = 1 ;
2019-06-10 17:30:25 +03:00
}
KWindowSystem : : setCurrentDesktop ( nextPos ) ;
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : switchToPreviousVirtualDesktop ( )
2019-06-10 17:30:25 +03:00
{
int desktops = KWindowSystem : : numberOfDesktops ( ) ;
if ( desktops < = 1 ) {
return ;
}
int curPos = KWindowSystem : : currentDesktop ( ) ;
int nextPos = curPos - 1 ;
2019-06-10 17:49:21 +03:00
if ( curPos = = 1 ) {
nextPos = desktops ;
2019-06-10 17:30:25 +03:00
}
KWindowSystem : : setCurrentDesktop ( nextPos ) ;
}
2021-06-12 14:18:03 +03:00
void XWindowInterface : : setWindowOnActivities ( const WindowId & wid , const QStringList & activities )
2018-01-13 12:55:13 +02:00
{
2021-06-12 14:18:03 +03:00
KWindowSystem : : setOnActivities ( wid . toUInt ( ) , activities ) ;
2018-01-13 12:55:13 +02:00
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : removeViewStruts ( QWindow & view )
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
}
2020-03-25 18:40:48 +02:00
WindowId XWindowInterface : : activeWindow ( )
2017-02-24 21:40:47 -05:00
{
return KWindowSystem : : self ( ) - > activeWindow ( ) ;
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : skipTaskBar ( const QDialog & dialog )
2017-02-24 21:40:47 -05:00
{
KWindowSystem : : setState ( dialog . winId ( ) , NET : : SkipTaskbar ) ;
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : slideWindow ( QWindow & view , AbstractWindowInterface : : Slide location )
2017-02-24 21:40:47 -05:00
{
auto slideLocation = KWindowEffects : : NoEdge ;
switch ( location ) {
2019-11-02 08:47:31 +02:00
case Slide : : Top :
slideLocation = KWindowEffects : : TopEdge ;
break ;
2017-02-24 21:40:47 -05:00
2019-11-02 08:47:31 +02:00
case Slide : : Bottom :
slideLocation = KWindowEffects : : BottomEdge ;
break ;
2017-02-24 21:40:47 -05:00
2019-11-02 08:47:31 +02:00
case Slide : : Left :
slideLocation = KWindowEffects : : LeftEdge ;
break ;
2017-02-24 21:40:47 -05:00
2019-11-02 08:47:31 +02:00
case Slide : : Right :
slideLocation = KWindowEffects : : RightEdge ;
break ;
2017-02-24 21:40:47 -05:00
2019-11-02 08:47:31 +02:00
default :
break ;
2017-02-24 21:40:47 -05:00
}
KWindowEffects : : slideWindow ( view . winId ( ) , slideLocation , - 1 ) ;
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : enableBlurBehind ( QWindow & view )
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
2020-03-25 18:40:48 +02:00
void XWindowInterface : : setActiveEdge ( QWindow * view , bool active )
2018-03-28 20:39:52 +03:00
{
2018-12-09 00:15:17 +02:00
ViewPart : : ScreenEdgeGhostWindow * window = qobject_cast < ViewPart : : ScreenEdgeGhostWindow * > ( view ) ;
2018-03-28 20:39:52 +03:00
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 ( ) ) {
2019-11-02 08:47:31 +02:00
case Plasma : : Types : : TopEdge :
value = 0 ;
break ;
2018-03-28 20:39:52 +03:00
2019-11-02 08:47:31 +02:00
case Plasma : : Types : : RightEdge :
value = 1 ;
break ;
2018-03-28 20:39:52 +03:00
2019-11-02 08:47:31 +02:00
case Plasma : : Types : : BottomEdge :
value = 2 ;
break ;
2018-03-28 20:39:52 +03:00
2019-11-02 08:47:31 +02:00
case Plasma : : Types : : LeftEdge :
value = 3 ;
break ;
2018-03-28 20:39:52 +03:00
2019-11-02 08:47:31 +02:00
case Plasma : : Types : : Floating :
default :
value = 4 ;
break ;
2018-03-28 20:39:52 +03:00
}
int hideType = 0 ;
value | = hideType < < 8 ;
xcb_change_property ( c , XCB_PROP_MODE_REPLACE , window - > winId ( ) , atom - > atom , XCB_ATOM_CARDINAL , 32 , 1 , & value ) ;
}
2020-03-06 21:45:54 +02:00
# if KF5_VERSION_MINOR >= 65
QRect XWindowInterface : : visibleGeometry ( const WindowId & wid , const QRect & frameGeometry ) const
{
NETWinInfo ni ( QX11Info : : connection ( ) , wid . toUInt ( ) , QX11Info : : appRootWindow ( ) , 0 , NET : : WM2GTKFrameExtents ) ;
NETStrut struts = ni . gtkFrameExtents ( ) ;
QMargins margins ( struts . left , struts . top , struts . right , struts . bottom ) ;
QRect visibleGeometry = frameGeometry ;
if ( ! margins . isNull ( ) ) {
visibleGeometry - = margins ;
}
return visibleGeometry ;
}
# endif
2020-05-11 17:31:03 +03:00
void XWindowInterface : : setFrameExtents ( QWindow * view , const QMargins & margins )
{
if ( ! view ) {
return ;
}
# if KF5_VERSION_MINOR >= 65
NETWinInfo ni ( QX11Info : : connection ( ) , view - > winId ( ) , QX11Info : : appRootWindow ( ) , 0 , NET : : WM2GTKFrameExtents ) ;
2020-05-12 14:45:31 +03:00
if ( margins . isNull ( ) ) {
//! delete property
xcb_connection_t * c = QX11Info : : connection ( ) ;
const QByteArray atomName = QByteArrayLiteral ( " _GTK_FRAME_EXTENTS " ) ;
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked ( c , false , atomName . length ( ) , atomName . constData ( ) ) ;
QScopedPointer < xcb_intern_atom_reply_t , QScopedPointerPodDeleter > atom ( xcb_intern_atom_reply ( c , atomCookie , nullptr ) ) ;
if ( ! atom ) {
return ;
}
// qDebug() << " deleting gtk frame extents atom..";
2020-05-11 17:31:03 +03:00
2020-05-12 14:45:31 +03:00
xcb_delete_property ( c , view - > winId ( ) , atom - > atom ) ;
} else {
NETStrut struts ;
struts . left = margins . left ( ) ;
struts . top = margins . top ( ) ;
struts . right = margins . right ( ) ;
struts . bottom = margins . bottom ( ) ;
ni . setGtkFrameExtents ( struts ) ;
}
2020-05-11 17:31:03 +03:00
2020-05-12 14:45:31 +03:00
/*NETWinInfo ni2(QX11Info::connection(), view->winId(), QX11Info::appRootWindow(), 0, NET::WM2GTKFrameExtents);
NETStrut applied = ni2 . gtkFrameExtents ( ) ;
2020-05-11 17:31:03 +03:00
QMargins amargins ( applied . left , applied . top , applied . right , applied . bottom ) ;
2020-05-12 14:45:31 +03:00
qDebug ( ) < < " window gtk frame extents applied :: " < < amargins ; */
2020-05-11 17:31:03 +03:00
# endif
}
2020-07-19 15:04:46 +03:00
void XWindowInterface : : checkShapeExtension ( )
{
if ( ! m_shapeExtensionChecked ) {
xcb_connection_t * c = QX11Info : : connection ( ) ;
xcb_prefetch_extension_data ( c , & xcb_shape_id ) ;
const xcb_query_extension_reply_t * extension = xcb_get_extension_data ( c , & xcb_shape_id ) ;
if ( extension - > present ) {
// query version
auto cookie = xcb_shape_query_version ( c ) ;
QScopedPointer < xcb_shape_query_version_reply_t , QScopedPointerPodDeleter > version ( xcb_shape_query_version_reply ( c , cookie , nullptr ) ) ;
if ( ! version . isNull ( ) ) {
m_shapeAvailable = ( version - > major_version * 0x10 + version - > minor_version ) > = 0x11 ;
}
}
m_shapeExtensionChecked = true ;
}
}
void XWindowInterface : : setInputMask ( QWindow * window , const QRect & rect )
{
if ( ! window | | ! window - > isVisible ( ) ) {
return ;
}
xcb_connection_t * c = QX11Info : : connection ( ) ;
if ( ! m_shapeExtensionChecked ) {
checkShapeExtension ( ) ;
}
if ( ! m_shapeAvailable ) {
return ;
}
if ( ! rect . isEmpty ( ) ) {
xcb_rectangle_t xcbrect ;
xcbrect . x = qMax ( SHRT_MIN , rect . x ( ) ) ;
xcbrect . y = qMax ( SHRT_MIN , rect . y ( ) ) ;
xcbrect . width = qMin ( ( int ) USHRT_MAX , rect . width ( ) ) ;
xcbrect . height = qMin ( ( int ) USHRT_MAX , rect . height ( ) ) ;
// set input shape, so that it doesn't accept any input events
xcb_shape_rectangles ( c , XCB_SHAPE_SO_SET , XCB_SHAPE_SK_INPUT ,
XCB_CLIP_ORDERING_UNSORTED , window - > winId ( ) , 0 , 0 , 1 , & xcbrect ) ;
} else {
// delete the shape
xcb_shape_mask ( c , XCB_SHAPE_SO_INTERSECT , XCB_SHAPE_SK_INPUT ,
window - > winId ( ) , 0 , 0 , XCB_PIXMAP_NONE ) ;
}
}
2020-05-11 17:31:03 +03:00
2020-03-25 18:40:48 +02:00
WindowInfoWrap XWindowInterface : : requestInfoActive ( )
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
}
2020-03-25 18:40:48 +02:00
WindowInfoWrap XWindowInterface : : requestInfo ( WindowId wid )
2016-12-25 09:25:27 +02:00
{
2019-06-01 11:04:23 +03:00
const KWindowInfo winfo { wid . value < WId > ( ) , NET : : WMFrameExtents
2019-11-02 08:47:31 +02:00
| NET : : WMWindowType
| NET : : WMGeometry
| NET : : WMDesktop
| NET : : WMState
| NET : : WMName
| NET : : WMVisibleName ,
NET : : WM2WindowClass
| NET : : WM2Activities
2020-01-22 13:02:00 +02:00
| NET : : WM2AllowedActions
2019-11-02 08:47:31 +02:00
| NET : : WM2TransientFor } ;
2019-06-01 11:04:23 +03:00
2016-12-28 02:57:31 -05:00
WindowInfoWrap winfoWrap ;
2017-01-16 14:07:49 -05:00
2020-07-19 18:03:55 +03:00
const auto winClass = QString ( winfo . windowClassName ( ) ) ;
//!used to track Plasma DesktopView windows because during startup can not be identified properly
bool plasmaBlockedWindow = ( winClass = = QLatin1String ( " plasmashell " ) & & ! isAcceptableWindow ( wid ) ) ;
if ( ! winfo . valid ( ) | | plasmaBlockedWindow ) {
2019-07-23 12:05:21 +03:00
winfoWrap . setIsValid ( false ) ;
2020-03-25 18:40:48 +02:00
} else if ( isValidWindow ( wid ) ) {
2017-01-02 01:04:10 -05:00
winfoWrap . setIsValid ( true ) ;
winfoWrap . setWid ( wid ) ;
2019-07-23 00:05:01 +03:00
winfoWrap . setParentId ( winfo . transientFor ( ) ) ;
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 ) ) ;
2019-05-31 17:44:04 +03:00
winfoWrap . setIsOnAllDesktops ( winfo . onAllDesktops ( ) ) ;
2019-06-08 00:31:30 +03:00
winfoWrap . setIsOnAllActivities ( winfo . activities ( ) . empty ( ) ) ;
2020-03-06 21:45:54 +02:00
# if KF5_VERSION_MINOR >= 65
winfoWrap . setGeometry ( visibleGeometry ( wid , winfo . frameGeometry ( ) ) ) ;
# else
2017-02-11 01:58:25 -05:00
winfoWrap . setGeometry ( winfo . frameGeometry ( ) ) ;
2020-03-06 21:45:54 +02:00
# endif
2017-12-24 20:54:45 +02:00
winfoWrap . setIsKeepAbove ( winfo . hasState ( NET : : KeepAbove ) ) ;
2019-12-26 16:53:37 +02:00
winfoWrap . setIsKeepBelow ( winfo . hasState ( NET : : KeepBelow ) ) ;
2020-04-07 13:00:45 +03:00
winfoWrap . setHasSkipPager ( winfo . hasState ( NET : : SkipPager ) ) ;
# if KF5_VERSION_MINOR >= 45
winfoWrap . setHasSkipSwitcher ( winfo . hasState ( NET : : SkipSwitcher ) ) ;
# endif
2018-10-28 10:03:22 +02:00
winfoWrap . setHasSkipTaskbar ( winfo . hasState ( NET : : SkipTaskbar ) ) ;
2019-06-08 00:31:30 +03:00
2020-02-29 17:57:59 +02:00
//! BEGIN:Window Abilities
2020-01-22 13:02:00 +02:00
winfoWrap . setIsClosable ( winfo . actionSupported ( NET : : ActionClose ) ) ;
winfoWrap . setIsFullScreenable ( winfo . actionSupported ( NET : : ActionFullScreen ) ) ;
winfoWrap . setIsMaximizable ( winfo . actionSupported ( NET : : ActionMax ) ) ;
winfoWrap . setIsMinimizable ( winfo . actionSupported ( NET : : ActionMinimize ) ) ;
winfoWrap . setIsMovable ( winfo . actionSupported ( NET : : ActionMove ) ) ;
winfoWrap . setIsResizable ( winfo . actionSupported ( NET : : ActionResize ) ) ;
winfoWrap . setIsShadeable ( winfo . actionSupported ( NET : : ActionShade ) ) ;
winfoWrap . setIsVirtualDesktopsChangeable ( winfo . actionSupported ( NET : : ActionChangeDesktop ) ) ;
2020-02-29 17:57:59 +02:00
//! END:Window Abilities
2020-01-22 13:02:00 +02:00
winfoWrap . setDisplay ( winfo . visibleName ( ) ) ;
2019-06-08 00:31:30 +03:00
winfoWrap . setDesktops ( { QString ( winfo . desktop ( ) ) } ) ;
winfoWrap . setActivities ( winfo . activities ( ) ) ;
2017-01-02 01:04:10 -05:00
}
2017-01-16 14:07:49 -05:00
2020-07-19 18:03:55 +03:00
if ( plasmaBlockedWindow ) {
windowRemoved ( wid ) ;
}
2016-12-28 02:57:31 -05:00
return winfoWrap ;
}
2020-03-25 18:40:48 +02:00
AppData XWindowInterface : : appDataFor ( WindowId wid )
2019-06-01 01:20:54 +03:00
{
return appDataFromUrl ( windowUrl ( wid ) ) ;
}
2020-03-25 18:40:48 +02:00
QUrl XWindowInterface : : windowUrl ( WindowId wid )
2019-06-01 01:20:54 +03:00
{
const KWindowInfo info ( wid . value < WId > ( ) , 0 , NET : : WM2WindowClass | NET : : WM2DesktopFileName ) ;
QString desktopFile = QString : : fromUtf8 ( info . desktopFileName ( ) ) ;
if ( ! desktopFile . isEmpty ( ) ) {
KService : : Ptr service = KService : : serviceByStorageId ( desktopFile ) ;
if ( service ) {
const QString & menuId = service - > menuId ( ) ;
// applications: URLs are used to refer to applications by their KService::menuId
// (i.e. .desktop file name) rather than the absolute path to a .desktop file.
if ( ! menuId . isEmpty ( ) ) {
return QUrl ( QStringLiteral ( " applications: " ) + menuId ) ;
}
return QUrl : : fromLocalFile ( service - > entryPath ( ) ) ;
}
if ( ! desktopFile . endsWith ( QLatin1String ( " .desktop " ) ) ) {
desktopFile . append ( QLatin1String ( " .desktop " ) ) ;
}
if ( KDesktopFile : : isDesktopFile ( desktopFile ) & & QFile : : exists ( desktopFile ) ) {
return QUrl : : fromLocalFile ( desktopFile ) ;
}
}
return windowUrlFromMetadata ( info . windowClassClass ( ) ,
2019-11-02 08:47:31 +02:00
NETWinInfo ( QX11Info : : connection ( ) , wid . value < WId > ( ) , QX11Info : : appRootWindow ( ) , NET : : WMPid , NET : : Properties2 ( ) ) . pid ( ) ,
rulesConfig , info . windowClassName ( ) ) ;
2019-06-01 01:20:54 +03:00
}
2020-03-25 18:40:48 +02:00
bool XWindowInterface : : windowCanBeDragged ( WindowId wid )
2018-10-28 10:03:22 +02:00
{
2020-01-22 13:02:00 +02:00
WindowInfoWrap winfo = requestInfo ( wid ) ;
return ( winfo . isValid ( )
& & ! winfo . isMinimized ( )
& & winfo . isMovable ( )
2020-02-29 17:57:59 +02:00
& & inCurrentDesktopActivity ( winfo ) ) ;
2018-10-28 10:03:22 +02:00
}
2020-03-25 18:40:48 +02:00
bool XWindowInterface : : windowCanBeMaximized ( WindowId wid )
2019-07-31 16:44:43 +03:00
{
2020-01-22 13:02:00 +02:00
WindowInfoWrap winfo = requestInfo ( wid ) ;
return ( winfo . isValid ( )
& & ! winfo . isMinimized ( )
& & winfo . isMaximizable ( )
2020-02-29 17:57:59 +02:00
& & inCurrentDesktopActivity ( winfo ) ) ;
2019-07-31 16:44:43 +03:00
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : requestActivate ( WindowId wid )
2019-04-01 23:50:25 +03:00
{
KWindowSystem : : activateWindow ( wid . toInt ( ) ) ;
}
2020-03-25 18:40:48 +02:00
QIcon XWindowInterface : : iconFor ( WindowId wid )
2019-05-27 18:56:24 +03:00
{
QIcon icon ;
icon . addPixmap ( KWindowSystem : : icon ( wid . value < WId > ( ) , KIconLoader : : SizeSmall , KIconLoader : : SizeSmall , false ) ) ;
icon . addPixmap ( KWindowSystem : : icon ( wid . value < WId > ( ) , KIconLoader : : SizeSmallMedium , KIconLoader : : SizeSmallMedium , false ) ) ;
icon . addPixmap ( KWindowSystem : : icon ( wid . value < WId > ( ) , KIconLoader : : SizeMedium , KIconLoader : : SizeMedium , false ) ) ;
icon . addPixmap ( KWindowSystem : : icon ( wid . value < WId > ( ) , KIconLoader : : SizeLarge , KIconLoader : : SizeLarge , false ) ) ;
return icon ;
}
2020-03-25 18:40:48 +02:00
WindowId XWindowInterface : : winIdFor ( QString appId , QRect geometry )
2019-04-01 23:50:25 +03:00
{
return activeWindow ( ) ;
}
2020-03-25 18:40:48 +02:00
WindowId XWindowInterface : : winIdFor ( QString appId , QString title )
2020-03-02 03:10:59 +02:00
{
return activeWindow ( ) ;
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : requestClose ( WindowId wid )
2019-06-02 01:07:47 +03:00
{
WindowInfoWrap wInfo = requestInfo ( wid ) ;
2020-02-29 17:57:59 +02:00
if ( ! wInfo . isValid ( ) ) {
2019-06-02 01:07:47 +03:00
return ;
}
NETRootInfo ri ( QX11Info : : connection ( ) , NET : : CloseWindow ) ;
ri . closeWindowRequest ( wInfo . wid ( ) . toUInt ( ) ) ;
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : requestMoveWindow ( WindowId wid , QPoint from )
2018-10-28 10:03:22 +02:00
{
2018-10-28 21:10:08 +02:00
WindowInfoWrap wInfo = requestInfo ( wid ) ;
2018-10-28 10:03:22 +02:00
2020-02-29 17:57:59 +02:00
if ( ! wInfo . isValid ( ) | | ! inCurrentDesktopActivity ( wInfo ) ) {
2018-10-28 10:03:22 +02:00
return ;
}
2019-11-02 08:47:31 +02:00
int borderX = wInfo . geometry ( ) . width ( ) > 120 ? 60 : 10 ;
2018-10-28 10:03:22 +02:00
int borderY { 10 } ;
//! find min/max values for x,y based on active window geometry
2018-10-28 21:10:08 +02:00
int minX = wInfo . geometry ( ) . x ( ) + borderX ;
int maxX = wInfo . geometry ( ) . x ( ) + wInfo . geometry ( ) . width ( ) - borderX ;
int minY = wInfo . geometry ( ) . y ( ) + borderY ;
int maxY = wInfo . geometry ( ) . y ( ) + wInfo . geometry ( ) . height ( ) - borderY ;
2018-10-28 10:03:22 +02:00
//! set the point from which this window will be moved,
//! make sure that it is in window boundaries
int validX = qBound ( minX , from . x ( ) , maxX ) ;
int validY = qBound ( minY , from . y ( ) , maxY ) ;
NETRootInfo ri ( QX11Info : : connection ( ) , NET : : WMMoveResize ) ;
2018-10-28 21:10:08 +02:00
ri . moveResizeRequest ( wInfo . wid ( ) . toUInt ( ) , validX , validY , NET : : Move ) ;
2018-10-28 10:03:22 +02:00
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : requestToggleIsOnAllDesktops ( WindowId wid )
2019-06-02 01:57:03 +03:00
{
WindowInfoWrap wInfo = requestInfo ( wid ) ;
2020-02-29 17:57:59 +02:00
if ( ! wInfo . isValid ( ) ) {
2019-06-02 01:57:03 +03:00
return ;
}
if ( KWindowSystem : : numberOfDesktops ( ) < = 1 ) {
return ;
}
if ( wInfo . isOnAllDesktops ( ) ) {
KWindowSystem : : setOnDesktop ( wid . toUInt ( ) , KWindowSystem : : currentDesktop ( ) ) ;
KWindowSystem : : forceActiveWindow ( wid . toUInt ( ) ) ;
} else {
KWindowSystem : : setOnAllDesktops ( wid . toUInt ( ) , true ) ;
}
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : requestToggleKeepAbove ( WindowId wid )
2019-06-02 01:07:47 +03:00
{
WindowInfoWrap wInfo = requestInfo ( wid ) ;
2020-02-29 17:57:59 +02:00
if ( ! wInfo . isValid ( ) ) {
2019-06-02 01:07:47 +03:00
return ;
}
NETWinInfo ni ( QX11Info : : connection ( ) , wid . toUInt ( ) , QX11Info : : appRootWindow ( ) , NET : : WMState , NET : : Properties2 ( ) ) ;
if ( wInfo . isKeepAbove ( ) ) {
2021-05-14 16:13:01 +02:00
ni . setState ( NET : : States ( ) , NET : : KeepAbove ) ;
2019-06-02 01:07:47 +03:00
} else {
2021-05-14 16:13:01 +02:00
ni . setState ( NET : : KeepAbove , NET : : KeepAbove ) ;
2019-06-02 01:07:47 +03:00
}
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : setKeepAbove ( WindowId wid , bool active )
2019-12-26 16:53:37 +02:00
{
2019-12-26 17:21:03 +02:00
if ( wid . toUInt ( ) < = 0 ) {
2019-12-26 16:53:37 +02:00
return ;
}
2019-12-26 17:21:03 +02:00
if ( active ) {
KWindowSystem : : setState ( wid . toUInt ( ) , NET : : KeepAbove ) ;
2019-12-26 21:10:11 +02:00
KWindowSystem : : clearState ( wid . toUInt ( ) , NET : : KeepBelow ) ;
2019-12-26 16:53:37 +02:00
} else {
2019-12-26 17:21:03 +02:00
KWindowSystem : : clearState ( wid . toUInt ( ) , NET : : KeepAbove ) ;
2019-12-26 16:53:37 +02:00
}
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : setKeepBelow ( WindowId wid , bool active )
2019-12-26 16:53:37 +02:00
{
2019-12-26 17:21:03 +02:00
if ( wid . toUInt ( ) < = 0 ) {
2019-12-26 16:53:37 +02:00
return ;
}
2019-12-26 17:21:03 +02:00
if ( active ) {
KWindowSystem : : setState ( wid . toUInt ( ) , NET : : KeepBelow ) ;
2019-12-26 21:10:11 +02:00
KWindowSystem : : clearState ( wid . toUInt ( ) , NET : : KeepAbove ) ;
2019-12-26 16:53:37 +02:00
} else {
2019-12-26 17:21:03 +02:00
KWindowSystem : : clearState ( wid . toUInt ( ) , NET : : KeepBelow ) ;
2019-12-26 16:53:37 +02:00
}
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : requestToggleMinimized ( WindowId wid )
2019-06-02 01:07:47 +03:00
{
WindowInfoWrap wInfo = requestInfo ( wid ) ;
2020-02-29 17:57:59 +02:00
if ( ! wInfo . isValid ( ) | | ! inCurrentDesktopActivity ( wInfo ) ) {
2019-06-02 01:07:47 +03:00
return ;
}
if ( wInfo . isMinimized ( ) ) {
2019-06-08 00:31:30 +03:00
bool onCurrent = wInfo . isOnDesktop ( m_currentDesktop ) ;
2019-06-02 01:07:47 +03:00
KWindowSystem : : unminimizeWindow ( wid . toUInt ( ) ) ;
if ( onCurrent ) {
KWindowSystem : : forceActiveWindow ( wid . toUInt ( ) ) ;
}
} else {
KWindowSystem : : minimizeWindow ( wid . toUInt ( ) ) ;
}
}
2020-03-25 18:40:48 +02:00
void XWindowInterface : : requestToggleMaximized ( WindowId wid )
2018-10-28 21:10:08 +02:00
{
2019-11-13 14:23:43 +02:00
WindowInfoWrap wInfo = requestInfo ( wid ) ;
if ( ! windowCanBeMaximized ( wid ) | | ! inCurrentDesktopActivity ( wInfo ) ) {
2019-07-31 16:44:43 +03:00
return ;
}
2018-10-28 21:10:08 +02:00
bool restore = wInfo . isMaxHoriz ( ) & & wInfo . isMaxVert ( ) ;
2019-06-02 01:07:47 +03:00
if ( wInfo . isMinimized ( ) ) {
KWindowSystem : : unminimizeWindow ( wid . toUInt ( ) ) ;
}
2018-10-28 21:10:08 +02:00
NETWinInfo ni ( QX11Info : : connection ( ) , wid . toInt ( ) , QX11Info : : appRootWindow ( ) , NET : : WMState , NET : : Properties2 ( ) ) ;
if ( restore ) {
ni . setState ( NET : : States ( ) , NET : : Max ) ;
} else {
ni . setState ( NET : : Max , NET : : Max ) ;
}
}
2017-01-02 01:04:10 -05:00
2020-03-25 18:40:48 +02:00
bool XWindowInterface : : isValidWindow ( WindowId wid )
2019-06-01 02:33:44 +03:00
{
2019-06-01 02:43:30 +03:00
if ( windowsTracker ( ) - > isValidFor ( wid ) ) {
return true ;
}
2020-03-25 18:40:48 +02:00
return isAcceptableWindow ( wid ) ;
2019-06-01 02:33:44 +03:00
}
2020-03-25 18:40:48 +02:00
bool XWindowInterface : : isAcceptableWindow ( WindowId wid )
2016-12-28 02:57:31 -05:00
{
2020-03-25 18:40:48 +02:00
const KWindowInfo info ( wid . toUInt ( ) , NET : : WMGeometry | NET : : WMState , NET : : WM2WindowClass ) ;
2019-06-01 03:44:42 +03:00
2020-03-02 10:05:43 +02:00
const auto winClass = QString ( info . windowClassName ( ) ) ;
2019-02-10 11:38:55 +02:00
2019-07-13 14:51:26 +03:00
//! ignored windows do not trackd
2020-03-25 18:40:48 +02:00
if ( hasBlockedTracking ( wid ) ) {
2020-03-05 21:20:25 +02:00
return false ;
2019-02-10 11:38:55 +02:00
}
2017-01-16 14:07:49 -05:00
2020-03-25 18:40:48 +02:00
//! whitelisted/approved windows
if ( isWhitelistedWindow ( wid ) ) {
return true ;
}
//! Window Checks
bool hasSkipTaskbar = info . hasState ( NET : : SkipTaskbar ) ;
bool hasSkipPager = info . hasState ( NET : : SkipPager ) ;
bool isSkipped = hasSkipTaskbar & & hasSkipPager ;
2020-03-26 14:16:04 +02:00
if ( isSkipped
& & ( ( winClass = = QLatin1String ( " yakuake " )
| | ( winClass = = QLatin1String ( " krunner " ) ) ) ) ) {
registerWhitelistedWindow ( wid ) ;
} else if ( winClass = = QLatin1String ( " plasmashell " ) ) {
2020-03-25 18:40:48 +02:00
if ( isSkipped & & isSidepanel ( info . geometry ( ) ) ) {
registerWhitelistedWindow ( wid ) ;
return true ;
} else if ( isPlasmaPanel ( info . geometry ( ) ) | | isFullScreenWindow ( info . geometry ( ) ) ) {
2020-02-29 17:57:59 +02:00
registerPlasmaIgnoredWindow ( wid ) ;
2020-03-05 21:20:25 +02:00
return false ;
2020-02-29 17:57:59 +02:00
}
2020-03-02 10:05:43 +02:00
} else if ( ( winClass = = QLatin1String ( " latte-dock " ) )
| | ( winClass = = QLatin1String ( " ksmserver " ) ) ) {
2020-03-25 18:40:48 +02:00
if ( isFullScreenWindow ( info . geometry ( ) ) ) {
registerIgnoredWindow ( wid ) ;
2020-03-05 21:20:25 +02:00
return false ;
2019-11-02 10:45:37 +02:00
}
2017-01-02 01:04:10 -05:00
}
2017-01-16 14:07:49 -05:00
2020-03-25 18:40:48 +02:00
return ! isSkipped ;
2020-03-05 21:20:25 +02:00
}
void XWindowInterface : : windowAddedProxy ( WId wid )
{
if ( ! isAcceptableWindow ( wid ) ) {
return ;
}
emit windowAdded ( wid ) ;
considerWindowChanged ( wid ) ;
}
void XWindowInterface : : windowChangedProxy ( WId wid , NET : : Properties prop1 , NET : : Properties2 prop2 )
{
2020-03-25 18:40:48 +02:00
if ( ! isValidWindow ( wid ) ) {
2020-03-05 21:20:25 +02:00
return ;
}
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....
2019-07-23 12:05:21 +03:00
if ( prop1 = = 0 & & ! ( prop2 & ( NET : : WM2Activities | NET : : WM2TransientFor ) ) ) {
2017-01-02 01:04:10 -05:00
return ;
}
2017-01-16 14:07:49 -05:00
2019-01-01 11:19:44 +02:00
//! accept only the following NET:Properties changed signals
2018-10-06 16:28:46 +03:00
//! NET::WMState, NET::WMGeometry, NET::ActiveWindow
2019-05-27 18:56:24 +03:00
if ( ! ( prop1 & NET : : WMState )
2019-11-02 08:47:31 +02:00
& & ! ( prop1 & NET : : WMGeometry )
& & ! ( prop1 & NET : : ActiveWindow )
& & ! ( prop1 & ( NET : : WMName | NET : : WMVisibleName )
& & ! ( prop2 & NET : : WM2TransientFor )
& & ! ( prop2 & NET : : WM2Activities ) ) ) {
2016-12-28 02:57:31 -05:00
return ;
2018-10-06 16:28:46 +03:00
}
2019-05-27 20:09:48 +03:00
considerWindowChanged ( wid ) ;
2016-12-25 09:25:27 +02:00
}
}
2019-05-11 15:43:10 +03:00
}