Skip to content

Commit 32cb146

Browse files
authored
Wayland Layer Shell (#2048)
* 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
1 parent 2ad883e commit 32cb146

File tree

4 files changed

+205
-47
lines changed

4 files changed

+205
-47
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ find_package(lxqt ${LXQT_MINIMUM_VERSION} REQUIRED)
4343
find_package(lxqt-globalkeys-ui ${LXQT_GLOBALKEYS_MINIMUM_VERSION} REQUIRED)
4444
find_package(lxqt-menu-data ${LXQT_MINIMUM_VERSION} REQUIRED)
4545

46+
find_package(LayerShellQt REQUIRED)
47+
4648
# Patch Version
4749
set(LXQT_PANEL_PATCH_VERSION 0)
4850
set(LXQT_PANEL_VERSION ${LXQT_MAJOR_VERSION}.${LXQT_MINOR_VERSION}.${LXQT_PANEL_PATCH_VERSION})

panel/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ target_link_libraries(${PROJECT}
103103
${LIBRARIES}
104104
${QTX_LIBRARIES}
105105
KF6::WindowSystem
106+
LayerShellQt::Interface
106107
${STATIC_PLUGINS}
107108
)
108109

panel/lxqtpanel.cpp

Lines changed: 196 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
#include <NETWM>
5454
#include <KWindowInfo>
5555

56+
#include <LayerShellQt/Window>
57+
5658
// Turn on this to show the time required to load each plugin during startup
5759
// #define DEBUG_PLUGIN_LOADTIME
5860
#ifdef DEBUG_PLUGIN_LOADTIME
@@ -141,6 +143,7 @@ LXQtPanel::LXQtPanel(const QString &configGroup, LXQt::Settings *settings, QWidg
141143
mAnimationTime(0),
142144
mReserveSpace(true),
143145
mAnimation(nullptr),
146+
mLayerWindow(nullptr),
144147
mLockPanel(false)
145148
{
146149
//You can find information about the flags and widget attributes in your
@@ -230,6 +233,37 @@ LXQtPanel::LXQtPanel(const QString &configGroup, LXQt::Settings *settings, QWidg
230233

231234
loadPlugins();
232235

236+
if(qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>())
237+
{
238+
// Create backing QWindow for LayerShellQt integration
239+
create();
240+
241+
if(!windowHandle())
242+
{
243+
qWarning() << "LXQtPanel: could not create QWindow for LayerShellQt integration.";
244+
}
245+
else
246+
{
247+
// Init Layer Shell (Must be done before showing widget)
248+
mLayerWindow = LayerShellQt::Window::get(windowHandle());
249+
mLayerWindow->setLayer(LayerShellQt::Window::LayerTop);
250+
251+
mLayerWindow->setScope(QStringLiteral("dock"));
252+
253+
LayerShellQt::Window::Anchors anchors;
254+
anchors.setFlag(LayerShellQt::Window::AnchorLeft);
255+
anchors.setFlag(LayerShellQt::Window::AnchorBottom);
256+
anchors.setFlag(LayerShellQt::Window::AnchorRight);
257+
mLayerWindow->setAnchors(anchors);
258+
259+
mLayerWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityOnDemand);
260+
mLayerWindow->setCloseOnDismissed(false);
261+
262+
mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorBottom);
263+
mLayerWindow->setExclusiveZone(height());
264+
}
265+
}
266+
233267
// NOTE: Some (X11) WMs may need the geometry to be set before QWidget::show().
234268
setPanelGeometry();
235269

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

481515
QRect rect;
516+
LayerShellQt::Window::Anchors anchors;
517+
LayerShellQt::Window::Anchor edge = LayerShellQt::Window::AnchorBottom;
482518

483519
if (isHorizontal())
484520
{
@@ -500,6 +536,7 @@ void LXQtPanel::setPanelGeometry(bool animate)
500536
switch (mAlignment)
501537
{
502538
case LXQtPanel::AlignmentLeft:
539+
anchors.setFlag(LayerShellQt::Window::AnchorLeft);
503540
rect.moveLeft(currentScreen.left());
504541
break;
505542

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

510547
case LXQtPanel::AlignmentRight:
548+
anchors.setFlag(LayerShellQt::Window::AnchorRight);
511549
rect.moveRight(currentScreen.right());
512550
break;
513551
}
514552

553+
if(lengthInPercents() && mLength == 100)
554+
{
555+
//Fill all available width
556+
anchors.setFlag(LayerShellQt::Window::AnchorLeft);
557+
anchors.setFlag(LayerShellQt::Window::AnchorRight);
558+
}
559+
515560
// Vert .......................
516561
if (mPosition == ILXQtPanel::PositionTop)
517562
{
563+
anchors.setFlag(LayerShellQt::Window::AnchorTop);
564+
edge = LayerShellQt::Window::AnchorTop;
565+
518566
if (mHidden)
519567
rect.moveBottom(currentScreen.top() + PANEL_HIDE_SIZE - 1);
520568
else
521569
rect.moveTop(currentScreen.top());
522570
}
523571
else
524572
{
573+
anchors.setFlag(LayerShellQt::Window::AnchorBottom);
574+
edge = LayerShellQt::Window::AnchorBottom;
575+
525576
if (mHidden)
526577
rect.moveTop(currentScreen.bottom() - PANEL_HIDE_SIZE + 1);
527578
else
@@ -548,6 +599,7 @@ void LXQtPanel::setPanelGeometry(bool animate)
548599
switch (mAlignment)
549600
{
550601
case LXQtPanel::AlignmentLeft:
602+
anchors.setFlag(LayerShellQt::Window::AnchorTop);
551603
rect.moveTop(currentScreen.top());
552604
break;
553605

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

558610
case LXQtPanel::AlignmentRight:
611+
anchors.setFlag(LayerShellQt::Window::AnchorBottom);
559612
rect.moveBottom(currentScreen.bottom());
560613
break;
561614
}
562615

616+
if(lengthInPercents() && mLength == 100)
617+
{
618+
//Fill all available width
619+
anchors.setFlag(LayerShellQt::Window::AnchorTop);
620+
anchors.setFlag(LayerShellQt::Window::AnchorBottom);
621+
}
622+
563623
// Horiz ......................
564624
if (mPosition == ILXQtPanel::PositionLeft)
565625
{
626+
anchors.setFlag(LayerShellQt::Window::AnchorLeft);
627+
edge = LayerShellQt::Window::AnchorLeft;
628+
566629
if (mHidden)
567630
rect.moveRight(currentScreen.left() + PANEL_HIDE_SIZE - 1);
568631
else
569632
rect.moveLeft(currentScreen.left());
570633
}
571634
else
572635
{
636+
anchors.setFlag(LayerShellQt::Window::AnchorRight);
637+
edge = LayerShellQt::Window::AnchorRight;
638+
573639
if (mHidden)
574640
rect.moveLeft(currentScreen.right() - PANEL_HIDE_SIZE + 1);
575641
else
576642
rect.moveRight(currentScreen.right());
577643
}
578644
}
645+
646+
if(mLayerWindow)
647+
{
648+
mLayerWindow->setAnchors(anchors);
649+
mLayerWindow->setExclusiveEdge(edge);
650+
651+
// Make LayerShell apply changes immediatly
652+
windowHandle()->requestUpdate();
653+
}
654+
579655
if (!mHidden || !mGeometry.isValid()) mGeometry = rect;
580656
if (rect != geometry())
581657
{
@@ -603,6 +679,37 @@ void LXQtPanel::setPanelGeometry(bool animate)
603679
setGeometry(rect);
604680
}
605681
}
682+
683+
if(mLayerWindow)
684+
{
685+
// Emulate auto-hide on Wayland
686+
// NOTE: we cannot move window out of screen so we make it smaller
687+
688+
// NOTE: a cleaner approach would be to use screen edge protocol
689+
// but it's specific to KWin
690+
691+
if(mHidden && LXQtPanelWidget->isVisible())
692+
{
693+
// Make it blank
694+
LXQtPanelWidget->hide();
695+
696+
// And make it small
697+
if(isHorizontal())
698+
resize(rect.width(), PANEL_HIDE_SIZE);
699+
else
700+
resize(PANEL_HIDE_SIZE, rect.height());
701+
}
702+
else if(!mHidden && !LXQtPanelWidget->isVisible())
703+
{
704+
// Restore contents
705+
LXQtPanelWidget->show();
706+
707+
// And make it big again
708+
resize(rect.size());
709+
}
710+
711+
updateWmStrut();
712+
}
606713
}
607714

608715
void LXQtPanel::setMargins()
@@ -664,62 +771,104 @@ void LXQtPanel::updateWmStrut()
664771
if(wid == 0 || !isVisible())
665772
return;
666773

667-
if (mReserveSpace && QApplication::primaryScreen())
774+
if(qGuiApp->nativeInterface<QNativeInterface::QX11Application>())
668775
{
669-
const QRect wholeScreen = QApplication::primaryScreen()->virtualGeometry();
670-
const QRect rect = geometry();
671-
// NOTE: https://standards.freedesktop.org/wm-spec/wm-spec-latest.html
672-
// Quote from the EWMH spec: " Note that the strut is relative to the screen edge, and not the edge of the xinerama monitor."
673-
// So, we use the geometry of the whole screen to calculate the strut rather than using the geometry of individual monitors.
674-
// Though the spec only mention Xinerama and did not mention XRandR, the rule should still be applied.
675-
// At least openbox is implemented like this.
676-
switch (mPosition)
776+
if (mReserveSpace && QApplication::primaryScreen())
677777
{
678-
case LXQtPanel::PositionTop:
679-
KX11Extras::setExtendedStrut(wid,
680-
/* Left */ 0, 0, 0,
681-
/* Right */ 0, 0, 0,
682-
/* Top */ rect.top() + getReserveDimension(), rect.left(), rect.right(),
683-
/* Bottom */ 0, 0, 0
684-
);
685-
break;
778+
const QRect wholeScreen = QApplication::primaryScreen()->virtualGeometry();
779+
const QRect rect = geometry();
780+
// NOTE: https://standards.freedesktop.org/wm-spec/wm-spec-latest.html
781+
// Quote from the EWMH spec: " Note that the strut is relative to the screen edge, and not the edge of the xinerama monitor."
782+
// So, we use the geometry of the whole screen to calculate the strut rather than using the geometry of individual monitors.
783+
// Though the spec only mention Xinerama and did not mention XRandR, the rule should still be applied.
784+
// At least openbox is implemented like this.
785+
switch (mPosition)
786+
{
787+
case LXQtPanel::PositionTop:
788+
KX11Extras::setExtendedStrut(wid,
789+
/* Left */ 0, 0, 0,
790+
/* Right */ 0, 0, 0,
791+
/* Top */ rect.top() + getReserveDimension(), rect.left(), rect.right(),
792+
/* Bottom */ 0, 0, 0
793+
);
794+
break;
686795

687-
case LXQtPanel::PositionBottom:
688-
KX11Extras::setExtendedStrut(wid,
689-
/* Left */ 0, 0, 0,
690-
/* Right */ 0, 0, 0,
691-
/* Top */ 0, 0, 0,
692-
/* Bottom */ wholeScreen.bottom() - rect.bottom() + getReserveDimension(), rect.left(), rect.right()
693-
);
694-
break;
796+
case LXQtPanel::PositionBottom:
797+
KX11Extras::setExtendedStrut(wid,
798+
/* Left */ 0, 0, 0,
799+
/* Right */ 0, 0, 0,
800+
/* Top */ 0, 0, 0,
801+
/* Bottom */ wholeScreen.bottom() - rect.bottom() + getReserveDimension(), rect.left(), rect.right()
802+
);
803+
break;
695804

696-
case LXQtPanel::PositionLeft:
697-
KX11Extras::setExtendedStrut(wid,
698-
/* Left */ rect.left() + getReserveDimension(), rect.top(), rect.bottom(),
699-
/* Right */ 0, 0, 0,
700-
/* Top */ 0, 0, 0,
701-
/* Bottom */ 0, 0, 0
702-
);
805+
case LXQtPanel::PositionLeft:
806+
KX11Extras::setExtendedStrut(wid,
807+
/* Left */ rect.left() + getReserveDimension(), rect.top(), rect.bottom(),
808+
/* Right */ 0, 0, 0,
809+
/* Top */ 0, 0, 0,
810+
/* Bottom */ 0, 0, 0
811+
);
703812

704-
break;
813+
break;
705814

706-
case LXQtPanel::PositionRight:
815+
case LXQtPanel::PositionRight:
816+
KX11Extras::setExtendedStrut(wid,
817+
/* Left */ 0, 0, 0,
818+
/* Right */ wholeScreen.right() - rect.right() + getReserveDimension(), rect.top(), rect.bottom(),
819+
/* Top */ 0, 0, 0,
820+
/* Bottom */ 0, 0, 0
821+
);
822+
break;
823+
}
824+
} else
825+
{
707826
KX11Extras::setExtendedStrut(wid,
708-
/* Left */ 0, 0, 0,
709-
/* Right */ wholeScreen.right() - rect.right() + getReserveDimension(), rect.top(), rect.bottom(),
710-
/* Top */ 0, 0, 0,
711-
/* Bottom */ 0, 0, 0
712-
);
713-
break;
827+
/* Left */ 0, 0, 0,
828+
/* Right */ 0, 0, 0,
829+
/* Top */ 0, 0, 0,
830+
/* Bottom */ 0, 0, 0
831+
);
832+
}
714833
}
715-
} else
834+
else if(mLayerWindow && qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>())
716835
{
717-
KX11Extras::setExtendedStrut(wid,
718-
/* Left */ 0, 0, 0,
719-
/* Right */ 0, 0, 0,
720-
/* Top */ 0, 0, 0,
721-
/* Bottom */ 0, 0, 0
722-
);
836+
//TODO: duplicated code, also set in setPanelGeometry()
837+
838+
if (mReserveSpace)
839+
{
840+
LayerShellQt::Window::Anchor edge = LayerShellQt::Window::AnchorBottom;
841+
842+
switch (mPosition)
843+
{
844+
case LXQtPanel::PositionTop:
845+
edge = LayerShellQt::Window::AnchorTop;
846+
break;
847+
848+
case LXQtPanel::PositionBottom:
849+
edge = LayerShellQt::Window::AnchorBottom;
850+
break;
851+
852+
case LXQtPanel::PositionLeft:
853+
edge = LayerShellQt::Window::AnchorLeft;
854+
break;
855+
856+
case LXQtPanel::PositionRight:
857+
edge = LayerShellQt::Window::AnchorRight;
858+
break;
859+
}
860+
861+
mLayerWindow->setExclusiveEdge(edge);
862+
mLayerWindow->setExclusiveZone(getReserveDimension());
863+
}
864+
else
865+
{
866+
mLayerWindow->setExclusiveEdge(LayerShellQt::Window::AnchorNone);
867+
mLayerWindow->setExclusiveZone(0);
868+
}
869+
870+
// Make LayerShellQt apply changes immediatly
871+
windowHandle()->requestUpdate();
723872
}
724873
}
725874

panel/lxqtpanel.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ class QMenu;
4242
class Plugin;
4343
class QAbstractItemModel;
4444

45+
namespace LayerShellQt {
46+
class Window;
47+
}
48+
4549
namespace LXQt {
4650
class Settings;
4751
class PluginInfo;
@@ -688,6 +692,8 @@ private slots:
688692
*/
689693
QPropertyAnimation *mAnimation;
690694

695+
LayerShellQt::Window *mLayerWindow;
696+
691697
/**
692698
* @brief Flag for providing the configuration options in panel's context menu
693699
*/

0 commit comments

Comments
 (0)