Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wayland Layer Shell #2048

Merged
merged 8 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
gfgit marked this conversation as resolved.
Show resolved Hide resolved

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);
}
gfgit marked this conversation as resolved.
Show resolved Hide resolved

// 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
Loading