Skip to content

Commit

Permalink
Wayland Layer Shell (#2048)
Browse files Browse the repository at this point in the history
* LXQtPanel: use LayerShell on Wayland

layer_shell protocol allows to ask for placement on Wayland

* LXQtPanel: fix position not applied immediatly on Wayland

* LXQtPanel: partially fix alignment on Wayland TODO

TODO: after changing length to pixels and back to percent
alignment is ignored and always kept to right

* LXQtPanel: fix auto-hide on Wayland

* LXQtPanel: set LayerShellQt KeyboardInteractivityOnDemand

Set layer shell keyboard interactivity on-demand

* LXQtPanel: ensure QWindow is created and null-check layer shell window

* LXQtPanel: silence warning on variable initialization

* LXQtPanel: fix again missing variable initialization
  • Loading branch information
gfgit authored Apr 9, 2024
1 parent 2ad883e commit 32cb146
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 47 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ find_package(lxqt ${LXQT_MINIMUM_VERSION} REQUIRED)
find_package(lxqt-globalkeys-ui ${LXQT_GLOBALKEYS_MINIMUM_VERSION} REQUIRED)
find_package(lxqt-menu-data ${LXQT_MINIMUM_VERSION} REQUIRED)

find_package(LayerShellQt REQUIRED)

# Patch Version
set(LXQT_PANEL_PATCH_VERSION 0)
set(LXQT_PANEL_VERSION ${LXQT_MAJOR_VERSION}.${LXQT_MINOR_VERSION}.${LXQT_PANEL_PATCH_VERSION})
Expand Down
1 change: 1 addition & 0 deletions panel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ target_link_libraries(${PROJECT}
${LIBRARIES}
${QTX_LIBRARIES}
KF6::WindowSystem
LayerShellQt::Interface
${STATIC_PLUGINS}
)

Expand Down
243 changes: 196 additions & 47 deletions panel/lxqtpanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
#include <NETWM>
#include <KWindowInfo>

#include <LayerShellQt/Window>

// Turn on this to show the time required to load each plugin during startup
// #define DEBUG_PLUGIN_LOADTIME
#ifdef DEBUG_PLUGIN_LOADTIME
Expand Down Expand Up @@ -141,6 +143,7 @@ LXQtPanel::LXQtPanel(const QString &configGroup, LXQt::Settings *settings, QWidg
mAnimationTime(0),
mReserveSpace(true),
mAnimation(nullptr),
mLayerWindow(nullptr),
mLockPanel(false)
{
//You can find information about the flags and widget attributes in your
Expand Down Expand Up @@ -230,6 +233,37 @@ LXQtPanel::LXQtPanel(const QString &configGroup, LXQt::Settings *settings, QWidg

loadPlugins();

if(qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>())
{
// Create backing QWindow for LayerShellQt integration
create();

if(!windowHandle())
{
qWarning() << "LXQtPanel: could not create QWindow for LayerShellQt integration.";
}
else
{
// Init Layer Shell (Must be done before showing widget)
mLayerWindow = LayerShellQt::Window::get(windowHandle());
mLayerWindow->setLayer(LayerShellQt::Window::LayerTop);

mLayerWindow->setScope(QStringLiteral("dock"));

LayerShellQt::Window::Anchors anchors;
anchors.setFlag(LayerShellQt::Window::AnchorLeft);
anchors.setFlag(LayerShellQt::Window::AnchorBottom);
anchors.setFlag(LayerShellQt::Window::AnchorRight);
mLayerWindow->setAnchors(anchors);

mLayerWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityOnDemand);
mLayerWindow->setCloseOnDismissed(false);

mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorBottom);
mLayerWindow->setExclusiveZone(height());
}
}

// NOTE: Some (X11) WMs may need the geometry to be set before QWidget::show().
setPanelGeometry();

Expand Down Expand Up @@ -479,6 +513,8 @@ void LXQtPanel::setPanelGeometry(bool animate)
const QRect currentScreen = screens.at(mActualScreenNum)->geometry();

QRect rect;
LayerShellQt::Window::Anchors anchors;
LayerShellQt::Window::Anchor edge = LayerShellQt::Window::AnchorBottom;

if (isHorizontal())
{
Expand All @@ -500,6 +536,7 @@ void LXQtPanel::setPanelGeometry(bool animate)
switch (mAlignment)
{
case LXQtPanel::AlignmentLeft:
anchors.setFlag(LayerShellQt::Window::AnchorLeft);
rect.moveLeft(currentScreen.left());
break;

Expand All @@ -508,20 +545,34 @@ void LXQtPanel::setPanelGeometry(bool animate)
break;

case LXQtPanel::AlignmentRight:
anchors.setFlag(LayerShellQt::Window::AnchorRight);
rect.moveRight(currentScreen.right());
break;
}

if(lengthInPercents() && mLength == 100)
{
//Fill all available width
anchors.setFlag(LayerShellQt::Window::AnchorLeft);
anchors.setFlag(LayerShellQt::Window::AnchorRight);
}

// Vert .......................
if (mPosition == ILXQtPanel::PositionTop)
{
anchors.setFlag(LayerShellQt::Window::AnchorTop);
edge = LayerShellQt::Window::AnchorTop;

if (mHidden)
rect.moveBottom(currentScreen.top() + PANEL_HIDE_SIZE - 1);
else
rect.moveTop(currentScreen.top());
}
else
{
anchors.setFlag(LayerShellQt::Window::AnchorBottom);
edge = LayerShellQt::Window::AnchorBottom;

if (mHidden)
rect.moveTop(currentScreen.bottom() - PANEL_HIDE_SIZE + 1);
else
Expand All @@ -548,6 +599,7 @@ void LXQtPanel::setPanelGeometry(bool animate)
switch (mAlignment)
{
case LXQtPanel::AlignmentLeft:
anchors.setFlag(LayerShellQt::Window::AnchorTop);
rect.moveTop(currentScreen.top());
break;

Expand All @@ -556,26 +608,50 @@ void LXQtPanel::setPanelGeometry(bool animate)
break;

case LXQtPanel::AlignmentRight:
anchors.setFlag(LayerShellQt::Window::AnchorBottom);
rect.moveBottom(currentScreen.bottom());
break;
}

if(lengthInPercents() && mLength == 100)
{
//Fill all available width
anchors.setFlag(LayerShellQt::Window::AnchorTop);
anchors.setFlag(LayerShellQt::Window::AnchorBottom);
}

// Horiz ......................
if (mPosition == ILXQtPanel::PositionLeft)
{
anchors.setFlag(LayerShellQt::Window::AnchorLeft);
edge = LayerShellQt::Window::AnchorLeft;

if (mHidden)
rect.moveRight(currentScreen.left() + PANEL_HIDE_SIZE - 1);
else
rect.moveLeft(currentScreen.left());
}
else
{
anchors.setFlag(LayerShellQt::Window::AnchorRight);
edge = LayerShellQt::Window::AnchorRight;

if (mHidden)
rect.moveLeft(currentScreen.right() - PANEL_HIDE_SIZE + 1);
else
rect.moveRight(currentScreen.right());
}
}

if(mLayerWindow)
{
mLayerWindow->setAnchors(anchors);
mLayerWindow->setExclusiveEdge(edge);

// Make LayerShell apply changes immediatly
windowHandle()->requestUpdate();
}

if (!mHidden || !mGeometry.isValid()) mGeometry = rect;
if (rect != geometry())
{
Expand Down Expand Up @@ -603,6 +679,37 @@ void LXQtPanel::setPanelGeometry(bool animate)
setGeometry(rect);
}
}

if(mLayerWindow)
{
// Emulate auto-hide on Wayland
// NOTE: we cannot move window out of screen so we make it smaller

// NOTE: a cleaner approach would be to use screen edge protocol
// but it's specific to KWin

if(mHidden && LXQtPanelWidget->isVisible())
{
// Make it blank
LXQtPanelWidget->hide();

// And make it small
if(isHorizontal())
resize(rect.width(), PANEL_HIDE_SIZE);
else
resize(PANEL_HIDE_SIZE, rect.height());
}
else if(!mHidden && !LXQtPanelWidget->isVisible())
{
// Restore contents
LXQtPanelWidget->show();

// And make it big again
resize(rect.size());
}

updateWmStrut();
}
}

void LXQtPanel::setMargins()
Expand Down Expand Up @@ -664,62 +771,104 @@ void LXQtPanel::updateWmStrut()
if(wid == 0 || !isVisible())
return;

if (mReserveSpace && QApplication::primaryScreen())
if(qGuiApp->nativeInterface<QNativeInterface::QX11Application>())
{
const QRect wholeScreen = QApplication::primaryScreen()->virtualGeometry();
const QRect rect = geometry();
// NOTE: https://standards.freedesktop.org/wm-spec/wm-spec-latest.html
// Quote from the EWMH spec: " Note that the strut is relative to the screen edge, and not the edge of the xinerama monitor."
// So, we use the geometry of the whole screen to calculate the strut rather than using the geometry of individual monitors.
// Though the spec only mention Xinerama and did not mention XRandR, the rule should still be applied.
// At least openbox is implemented like this.
switch (mPosition)
if (mReserveSpace && QApplication::primaryScreen())
{
case LXQtPanel::PositionTop:
KX11Extras::setExtendedStrut(wid,
/* Left */ 0, 0, 0,
/* Right */ 0, 0, 0,
/* Top */ rect.top() + getReserveDimension(), rect.left(), rect.right(),
/* Bottom */ 0, 0, 0
);
break;
const QRect wholeScreen = QApplication::primaryScreen()->virtualGeometry();
const QRect rect = geometry();
// NOTE: https://standards.freedesktop.org/wm-spec/wm-spec-latest.html
// Quote from the EWMH spec: " Note that the strut is relative to the screen edge, and not the edge of the xinerama monitor."
// So, we use the geometry of the whole screen to calculate the strut rather than using the geometry of individual monitors.
// Though the spec only mention Xinerama and did not mention XRandR, the rule should still be applied.
// At least openbox is implemented like this.
switch (mPosition)
{
case LXQtPanel::PositionTop:
KX11Extras::setExtendedStrut(wid,
/* Left */ 0, 0, 0,
/* Right */ 0, 0, 0,
/* Top */ rect.top() + getReserveDimension(), rect.left(), rect.right(),
/* Bottom */ 0, 0, 0
);
break;

case LXQtPanel::PositionBottom:
KX11Extras::setExtendedStrut(wid,
/* Left */ 0, 0, 0,
/* Right */ 0, 0, 0,
/* Top */ 0, 0, 0,
/* Bottom */ wholeScreen.bottom() - rect.bottom() + getReserveDimension(), rect.left(), rect.right()
);
break;
case LXQtPanel::PositionBottom:
KX11Extras::setExtendedStrut(wid,
/* Left */ 0, 0, 0,
/* Right */ 0, 0, 0,
/* Top */ 0, 0, 0,
/* Bottom */ wholeScreen.bottom() - rect.bottom() + getReserveDimension(), rect.left(), rect.right()
);
break;

case LXQtPanel::PositionLeft:
KX11Extras::setExtendedStrut(wid,
/* Left */ rect.left() + getReserveDimension(), rect.top(), rect.bottom(),
/* Right */ 0, 0, 0,
/* Top */ 0, 0, 0,
/* Bottom */ 0, 0, 0
);
case LXQtPanel::PositionLeft:
KX11Extras::setExtendedStrut(wid,
/* Left */ rect.left() + getReserveDimension(), rect.top(), rect.bottom(),
/* Right */ 0, 0, 0,
/* Top */ 0, 0, 0,
/* Bottom */ 0, 0, 0
);

break;
break;

case LXQtPanel::PositionRight:
case LXQtPanel::PositionRight:
KX11Extras::setExtendedStrut(wid,
/* Left */ 0, 0, 0,
/* Right */ wholeScreen.right() - rect.right() + getReserveDimension(), rect.top(), rect.bottom(),
/* Top */ 0, 0, 0,
/* Bottom */ 0, 0, 0
);
break;
}
} else
{
KX11Extras::setExtendedStrut(wid,
/* Left */ 0, 0, 0,
/* Right */ wholeScreen.right() - rect.right() + getReserveDimension(), rect.top(), rect.bottom(),
/* Top */ 0, 0, 0,
/* Bottom */ 0, 0, 0
);
break;
/* Left */ 0, 0, 0,
/* Right */ 0, 0, 0,
/* Top */ 0, 0, 0,
/* Bottom */ 0, 0, 0
);
}
}
} else
else if(mLayerWindow && qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>())
{
KX11Extras::setExtendedStrut(wid,
/* Left */ 0, 0, 0,
/* Right */ 0, 0, 0,
/* Top */ 0, 0, 0,
/* Bottom */ 0, 0, 0
);
//TODO: duplicated code, also set in setPanelGeometry()

if (mReserveSpace)
{
LayerShellQt::Window::Anchor edge = LayerShellQt::Window::AnchorBottom;

switch (mPosition)
{
case LXQtPanel::PositionTop:
edge = LayerShellQt::Window::AnchorTop;
break;

case LXQtPanel::PositionBottom:
edge = LayerShellQt::Window::AnchorBottom;
break;

case LXQtPanel::PositionLeft:
edge = LayerShellQt::Window::AnchorLeft;
break;

case LXQtPanel::PositionRight:
edge = LayerShellQt::Window::AnchorRight;
break;
}

mLayerWindow->setExclusiveEdge(edge);
mLayerWindow->setExclusiveZone(getReserveDimension());
}
else
{
mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorNone);
mLayerWindow->setExclusiveZone(0);
}

// Make LayerShellQt apply changes immediatly
windowHandle()->requestUpdate();
}
}

Expand Down
6 changes: 6 additions & 0 deletions panel/lxqtpanel.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class QMenu;
class Plugin;
class QAbstractItemModel;

namespace LayerShellQt {
class Window;
}

namespace LXQt {
class Settings;
class PluginInfo;
Expand Down Expand Up @@ -688,6 +692,8 @@ private slots:
*/
QPropertyAnimation *mAnimation;

LayerShellQt::Window *mLayerWindow;

/**
* @brief Flag for providing the configuration options in panel's context menu
*/
Expand Down

0 comments on commit 32cb146

Please sign in to comment.