Compare commits

...

5 Commits

Author SHA1 Message Date
28131eea16 Allow specifying explicit desired screen
If the window position is not specified, which is a reasonable thing to
do when using the layer shell protocol, setWidth() and setHeight() can
unintentionally change the screen() to the wrong one.

This change adds a setDesiredScreen() function so it's harder to shoot
yourself in the foot while using the layer shell protocol.
2025-11-19 16:39:43 +02:00
b9e1260949 Window: Ensure we integrate windows that were already were shown in a different shell 2025-10-25 13:02:31 +02:00
cb79d3f60a window: Improve how we tell the window to do layer-shell
Instead of telling it in the construction after forcing the window
creation, install an event handler that sets it when we get the
PlatformSurface event.

It has the advantage that it will also trigger in subsequent platform
surface events (e.g. after hide and show).
2025-10-25 13:02:31 +02:00
2b8544f5a0 Drop obsolete code for Qt versions lower than 6.9 2025-10-16 14:02:29 +02:00
e4b5bdbf1b Update version for new release 6.5.80 2025-09-18 20:56:39 +05:30
9 changed files with 150 additions and 144 deletions

View File

@ -4,7 +4,7 @@
cmake_minimum_required(VERSION 3.16)
project(layershellqt)
set(PROJECT_VERSION "6.4.90")
set(PROJECT_VERSION "6.5.80")
set(PROJECT_VERSION_MAJOR 6)
set(CMAKE_C_STANDARD 99)

View File

@ -4,7 +4,7 @@
ecm_add_qml_module(LayerShellQtQml
URI "org.kde.layershell"
VERSION 1.0
SOURCES types.h types.cpp
SOURCES types.h
GENERATE_PLUGIN_SOURCE)
target_link_libraries(LayerShellQtQml PRIVATE Qt::Qml LayerShellQtInterface)

View File

@ -1,49 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <aleix.pol_gonzalez@mercedes-benz.com>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "types.h"
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 1)
int QQmlMarginsValueType::left() const
{
return m.left();
}
int QQmlMarginsValueType::top() const
{
return m.top();
}
int QQmlMarginsValueType::right() const
{
return m.right();
}
int QQmlMarginsValueType::bottom() const
{
return m.bottom();
}
void QQmlMarginsValueType::setLeft(int left)
{
m.setLeft(left);
}
void QQmlMarginsValueType::setTop(int top)
{
m.setTop(top);
}
void QQmlMarginsValueType::setRight(int right)
{
m.setRight(right);
}
void QQmlMarginsValueType::setBottom(int bottom)
{
m.setBottom(bottom);
}
#endif

View File

@ -17,39 +17,3 @@ class WindowForeign
QML_UNCREATABLE("")
QML_ATTACHED(LayerShellQt::Window)
};
// available upstream since https://invent.kde.org/qt/qt/qtdeclarative/-/commit/a398101f715bfc447aa889fc9c58b13bfe75ab47
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 1)
struct Q_QML_EXPORT QQmlMarginsValueType {
QMargins m;
Q_PROPERTY(int left READ left WRITE setLeft FINAL)
Q_PROPERTY(int right READ right WRITE setRight FINAL)
Q_PROPERTY(int top READ top WRITE setTop FINAL)
Q_PROPERTY(int bottom READ bottom WRITE setBottom FINAL)
Q_GADGET
QML_ANONYMOUS
QML_FOREIGN(QMargins)
QML_EXTENDED(QQmlMarginsValueType)
QML_STRUCTURED_VALUE
public:
QQmlMarginsValueType() = default;
Q_INVOKABLE QQmlMarginsValueType(const QMarginsF &margins)
: m(margins.toMargins())
{
}
int left() const;
int right() const;
int top() const;
int bottom() const;
void setLeft(int);
void setRight(int);
void setTop(int);
void setBottom(int);
operator QMargins() const
{
return m;
}
};
#endif

View File

@ -9,6 +9,7 @@
#include <layershellqt_logging.h>
#include <QPlatformSurfaceEvent>
#include <QPointer>
#include <optional>
@ -33,7 +34,8 @@ public:
Window::Layer layer = Window::LayerTop;
QMargins margins;
QSize desiredSize = QSize(0, 0);
Window::ScreenConfiguration screenConfiguration = Window::ScreenFromQWindow;
QPointer<QScreen> desiredScreen;
bool desiredActiveScreen = false;
bool closeOnDismissed = true;
bool activateOnShow = true;
};
@ -151,14 +153,46 @@ Window::Layer Window::layer() const
return d->layer;
}
Window::ScreenConfiguration Window::screenConfiguration() const
void Window::setDesiredActiveScreen(bool set)
{
return d->screenConfiguration;
if (d->desiredActiveScreen == set) {
return;
}
d->desiredActiveScreen = set;
if (d->desiredActiveScreen && d->desiredScreen) {
d->desiredScreen = nullptr;
Q_EMIT desiredScreenChanged();
}
Q_EMIT desiredActiveScreenChanged();
}
void Window::setScreenConfiguration(Window::ScreenConfiguration screenConfiguration)
bool Window::desiredActiveScreen() const
{
d->screenConfiguration = screenConfiguration;
return d->desiredActiveScreen;
}
void Window::setDesiredScreen(QScreen *screen)
{
if (d->desiredScreen == screen) {
return;
}
d->desiredScreen = screen;
if (d->desiredScreen && d->desiredActiveScreen) {
d->desiredActiveScreen = false;
Q_EMIT desiredActiveScreenChanged();
}
Q_EMIT desiredScreenChanged();
}
QScreen *Window::desiredScreen() const
{
return d->desiredScreen;
}
bool Window::closeOnDismissed() const
@ -186,12 +220,35 @@ Window::Window(QWindow *window)
, d(new WindowPrivate(window))
{
s_map.insert(d->parentWindow, this);
window->installEventFilter(this);
if (window->isVisible()) {
qCWarning(LAYERSHELLQT) << d->parentWindow << "already has a shell integration. Call QWindow::close() first and show it again.";
}
window->create();
if (window->handle()) {
initializeShell();
}
}
auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle());
bool Window::eventFilter(QObject *watched, QEvent *event)
{
auto window = qobject_cast<QWindow *>(watched);
if (!window) {
return false;
}
if (event->type() == QEvent::PlatformSurface) {
if (auto pse = static_cast<QPlatformSurfaceEvent *>(event); pse->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
initializeShell();
}
}
return false;
}
void Window::initializeShell()
{
auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(d->parentWindow->handle());
if (!waylandWindow) {
qCWarning(LAYERSHELLQT) << window << "is not a wayland window. Not creating zwlr_layer_surface";
qCWarning(LAYERSHELLQT) << d->parentWindow << "is not a wayland window. Not creating zwlr_layer_surface";
return;
}
@ -205,7 +262,6 @@ Window::Window(QWindow *window)
return;
}
}
waylandWindow->setShellIntegration(shellIntegration);
}

View File

@ -27,8 +27,9 @@ class LAYERSHELLQT_EXPORT Window : public QObject
Q_PROPERTY(qint32 exclusionZone READ exclusionZone WRITE setExclusiveZone NOTIFY exclusionZoneChanged)
Q_PROPERTY(Layer layer READ layer WRITE setLayer NOTIFY layerChanged)
Q_PROPERTY(KeyboardInteractivity keyboardInteractivity READ keyboardInteractivity WRITE setKeyboardInteractivity NOTIFY keyboardInteractivityChanged)
Q_PROPERTY(ScreenConfiguration screenConfiguration READ screenConfiguration WRITE setScreenConfiguration)
Q_PROPERTY(bool activateOnShow READ activateOnShow WRITE setActivateOnShow)
Q_PROPERTY(bool desiredActiveScreen READ desiredActiveScreen WRITE setDesiredActiveScreen NOTIFY desiredActiveScreenChanged)
Q_PROPERTY(QScreen *desiredScreen READ desiredScreen WRITE setDesiredScreen NOTIFY desiredScreenChanged)
public:
~Window() override;
@ -64,17 +65,6 @@ public:
};
Q_ENUM(KeyboardInteractivity)
/**
* This enum type is used to specify which screen to place the surface on.
* ScreenFromQWindow (the default) reads QWindow::screen() while ScreenFromCompositor
* passes nil and lets the compositor decide.
*/
enum ScreenConfiguration {
ScreenFromQWindow = 0,
ScreenFromCompositor = 1,
};
Q_ENUM(ScreenConfiguration)
void setAnchors(Anchors anchor);
Anchors anchors() const;
@ -96,8 +86,33 @@ public:
void setLayer(Layer layer);
Layer layer() const;
void setScreenConfiguration(ScreenConfiguration screenConfiguration);
ScreenConfiguration screenConfiguration() const;
/**
* Indicates whether the layer shell surface should be placed on the active screen based on @a set.
*
* The active screen depends on the compositor policies.
*
* If no explicit desired screen has been specified with the setDesiredScreen() function or the layer
* surface doesn't need to be placed on the active screen, i.e. setDesiredActiveScreen() has not
* been called, the QWindow::screen() will be used to decide what screen the layer shell surface
* should be placed on.
*
* The desiredScreen() will be reset if @a set is @c true.
*/
void setDesiredActiveScreen(bool set);
bool desiredActiveScreen() const;
/**
* Indicates that the layer shell surface should be placed on the specified @a screen.
*
* If no explicit desired screen has been specified with the setDesiredScreen() function or the layer
* surface doesn't need to be placed on the active screen, i.e. setDesiredActiveScreen() has not
* been called, the QWindow::screen() will be used to decide what screen the layer shell surface
* should be placed on.
*
* The desiredActiveScreen() will be reset to @c false after calling this function.
*/
void setDesiredScreen(QScreen *screen);
QScreen *desiredScreen() const;
/**
* Sets a string based identifier for this window.
@ -138,6 +153,8 @@ public:
static Window *qmlAttachedProperties(QObject *object);
bool eventFilter(QObject *watched, QEvent *event) override;
Q_SIGNALS:
void anchorsChanged();
void exclusionZoneChanged();
@ -146,8 +163,12 @@ Q_SIGNALS:
void desiredSizeChanged();
void keyboardInteractivityChanged();
void layerChanged();
void desiredActiveScreenChanged();
void desiredScreenChanged();
private:
void initializeShell();
Window(QWindow *window);
QScopedPointer<WindowPrivate> d;
};

View File

@ -26,7 +26,12 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell,
, m_window(window)
{
wl_output *output = nullptr;
if (m_interface->screenConfiguration() == Window::ScreenFromQWindow) {
if (!m_interface->desiredActiveScreen()) {
QScreen *desiredScreen = m_interface->desiredScreen();
if (!desiredScreen) {
window->window()->screen();
}
auto waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen *>(window->window()->screen()->handle());
// Qt will always assign a screen to a window, but if the compositor has no screens available a dummy QScreen object is created
// this will not cast to a QWaylandScreen
@ -124,13 +129,7 @@ void QWaylandLayerSurface::attachPopup(QtWaylandClient::QWaylandShellSurface *po
void QWaylandLayerSurface::applyConfigure()
{
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
m_configuring = true;
#endif
window()->resizeFromApplyConfigure(m_pendingSize);
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
m_configuring = false;
#endif
}
void QWaylandLayerSurface::setDesiredSize(const QSize &size)
@ -181,25 +180,12 @@ void QWaylandLayerSurface::setLayer(uint32_t layer)
set_layer(layer);
}
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
void QWaylandLayerSurface::setWindowGeometry(const QRect &geometry)
{
if (m_configuring) {
return;
}
if (m_interface->desiredSize().isNull()) {
setDesiredSize(geometry.size());
}
}
#else
void QWaylandLayerSurface::setWindowSize(const QSize &size)
{
if (m_interface->desiredSize().isNull()) {
setDesiredSize(size);
}
}
#endif
bool QWaylandLayerSurface::requestActivate()
{
@ -268,13 +254,6 @@ void QWaylandLayerSurface::requestXdgActivationToken(quint32 serial)
void QWaylandLayerSurface::sendExpose()
{
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
window()->handleExpose(QRect(QPoint(), m_pendingSize));
#elif QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
window()->sendRecursiveExposeEvent();
#else
window()->updateExposure();
#endif
}
}

View File

@ -43,11 +43,7 @@ public:
void setLayer(uint32_t layer);
void applyConfigure() override;
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
void setWindowGeometry(const QRect &geometry) override;
#else
void setWindowSize(const QSize &size) override;
#endif
bool requestActivate() override;
bool requestActivateOnShow() override;
@ -66,9 +62,6 @@ private:
QString m_activationToken;
bool m_configured = false;
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
bool m_configuring = false;
#endif
};
}

View File

@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2025 Aleix Pol i Gonzalez <aleixpol@kde.org>
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
import QtQuick
import QtQuick.Controls
import org.kde.layershell 1.0 as LayerShell
Item
{
Button {
text: "Convert"
anchors.centerIn: parent
onClicked: {
win.close()
win.LayerShell.Window.anchors = LayerShell.Window.AnchorLeft;
win.LayerShell.Window.layer = LayerShell.Window.LayerTop;
win.LayerShell.Window.exclusionZone = -1;
win.show()
}
}
Window {
id: win
width: 100
height: 100
Rectangle {
anchors.fill: parent
color: "red"
Text {
anchors.centerIn: parent
text: "top"
}
}
visible: true
}
}