Compare commits

..

2 Commits

Author SHA1 Message Date
9fe00051e0 debug++ 2024-04-08 11:49:36 +01:00
c51ab381da deferred apply
When we get a configure event defer applying it until all pending
callbacks are processsed. This ensures we only only process configure
events that match the last thing we requested.
2024-04-08 11:42:08 +01:00
21 changed files with 192 additions and 413 deletions

View File

@ -7,6 +7,3 @@ include:
- /gitlab-templates/reuse-lint.yml
- /gitlab-templates/linux-qt6.yml
- /gitlab-templates/freebsd-qt6.yml
- /gitlab-templates/xml-lint.yml
- /gitlab-templates/yaml-lint.yml
- /gitlab-templates/linux-qt6-next.yml

View File

@ -2,11 +2,8 @@
# SPDX-License-Identifier: CC0-1.0
Dependencies:
- 'on': ['@all']
'require':
- 'on': ['@all']
'require':
'frameworks/extra-cmake-modules': '@latest-kf6'
'third-party/wayland': '@latest'
'third-party/wayland-protocols': '@latest'
Options:
require-passing-tests-on: ['Linux', 'FreeBSD', 'Windows']

View File

@ -4,13 +4,13 @@
cmake_minimum_required(VERSION 3.16)
project(layershellqt)
set(PROJECT_VERSION "6.6.80")
set(PROJECT_VERSION "6.0.80")
set(PROJECT_VERSION_MAJOR 6)
set(CMAKE_C_STANDARD 99)
set(QT_MIN_VERSION "6.10.0")
set(KF6_MIN_VERSION "6.22.0")
set(QT_MIN_VERSION "6.6.0")
set(KF6_MIN_VERSION "6.0.0")
set(KDE_COMPILERSETTINGS_LEVEL "5.82")
set(CMAKE_CXX_STANDARD 20)
@ -31,15 +31,9 @@ include(GenerateExportHeader)
include(KDEClangFormat)
include(ECMQtDeclareLoggingCategory)
include(ECMQmlModule)
include(KDEGitCommitHooks)
include(ECMGenerateExportHeader)
find_package(Qt6 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS WaylandClient Qml)
if (Qt6WaylandClient_VERSION VERSION_GREATER_EQUAL "6.10.0")
find_package(Qt6WaylandClientPrivate ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
endif()
find_package(WaylandScanner REQUIRED)
find_package(Wayland 1.3 COMPONENTS Client Server)
find_package(WaylandProtocols REQUIRED)
@ -59,10 +53,8 @@ ecm_setup_version(${PROJECT_VERSION} VARIABLE_PREFIX LAYERSHELLQT
file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h)
kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
ecm_set_disabled_deprecation_versions(QT 6.8.1
KF 6.9.0
ecm_set_disabled_deprecation_versions(QT 6.5
KF 5.240
)
add_subdirectory(src)

View File

@ -2,7 +2,7 @@
# SPDX-License-Identifier: CC0-1.0
maintainer:
- vladz
- vladz
description: Layer Shell Qt
platforms:
- name: Linux

View File

@ -4,18 +4,10 @@
remove_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS)
add_library(LayerShellQtInterface)
qt6_extract_metatypes(LayerShellQtInterface)
if (Qt6_VERSION VERSION_GREATER_EQUAL "6.8.0")
set(private_code_option "PRIVATE_CODE")
endif()
qt6_generate_wayland_protocol_client_sources(LayerShellQtInterface
${private_code_option}
FILES
${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml
${WaylandProtocols_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/wlr-layer-shell-unstable-v1.xml
qt6_generate_wayland_protocol_client_sources(LayerShellQtInterface FILES
${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml
${WaylandProtocols_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml
${CMAKE_CURRENT_SOURCE_DIR}/wlr-layer-shell-unstable-v1.xml
)
ecm_qt_declare_logging_category(LayerShellQtInterface
@ -64,12 +56,10 @@ ecm_generate_headers(LayerShellQt_HEADERS
)
ecm_generate_export_header(LayerShellQtInterface
generate_export_header(LayerShellQtInterface
BASE_NAME LayerShellQtInterface
EXPORT_MACRO_NAME LAYERSHELLQT_EXPORT
EXPORT_FILE_NAME LayerShellQt/layershellqt_export.h
VERSION ${LAYERSHELLQT_VERSION}
DEPRECATION_VERSIONS 6.6
)
install(TARGETS layer-shell

View File

@ -4,8 +4,7 @@
ecm_add_qml_module(LayerShellQtQml
URI "org.kde.layershell"
VERSION 1.0
SOURCES types.h
GENERATE_PLUGIN_SOURCE)
SOURCES layershellqtplugin.cpp)
target_link_libraries(LayerShellQtQml PRIVATE Qt::Qml LayerShellQtInterface)
ecm_finalize_qml_module(LayerShellQtQml DESTINATION ${KDE_INSTALL_QMLDIR})
ecm_finalize_qml_module(LayerShellQtQml DESTINATION ${KDE_INSTALL_QMLDIR})

View File

@ -0,0 +1,24 @@
/*
* 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 <QQmlExtensionPlugin>
#include "../interfaces/window.h"
#include <qqml.h>
QML_DECLARE_TYPEINFO(LayerShellQt::Window, QML_HAS_ATTACHED_PROPERTIES)
class Plugin : public QQmlExtensionPlugin
{
Q_PLUGIN_METADATA(IID "org.kde.layershellqt")
Q_OBJECT
public:
void registerTypes(const char *uri) override {
Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.layershell"));
qmlRegisterType<LayerShellQt::Window>(uri, 1, 0, "Window");
}
};
#include "layershellqtplugin.moc"

View File

@ -1,19 +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 "../interfaces/window.h"
#include <qqml.h>
QML_DECLARE_TYPEINFO(LayerShellQt::Window, QML_HAS_ATTACHED_PROPERTIES)
class WindowForeign
{
Q_GADGET
QML_NAMED_ELEMENT(Window)
QML_FOREIGN(LayerShellQt::Window)
QML_UNCREATABLE("")
QML_ATTACHED(LayerShellQt::Window)
};

View File

@ -5,9 +5,6 @@
*/
#include "shell.h"
#if LAYERSHELLQTINTERFACE_BUILD_DEPRECATED_SINCE(6, 6)
#include <QByteArray>
#include <layershellqt_logging.h>
#include <qglobal.h>
@ -21,4 +18,3 @@ void Shell::useLayerShell()
qCDebug(LAYERSHELLQT) << "Unable to set QT_WAYLAND_SHELL_INTEGRATION=layer-shell";
}
}
#endif

View File

@ -8,9 +8,7 @@
#define LAYERSHELLQTSHELL_H
#include "layershellqt_export.h"
#if LAYERSHELLQTINTERFACE_ENABLE_DEPRECATED_SINCE(6, 6)
#include "window.h"
#include <QString>
namespace LayerShellQt
@ -21,11 +19,9 @@ namespace LayerShellQt
class LAYERSHELLQT_EXPORT Shell
{
public:
LAYERSHELLQTINTERFACE_DEPRECATED_VERSION(6, 6, "Calling useLayerShell is not needed since Qt 6.5")
static void useLayerShell();
};
}
#endif
#endif

View File

@ -7,14 +7,13 @@
#include "window.h"
#include "../qwaylandlayershellintegration_p.h"
#include <QtWaylandClient/private/qwaylandclientshellapi_p.h>
#include <layershellqt_logging.h>
#include <QPlatformSurfaceEvent>
#include <QPointer>
#include <mutex>
#include <optional>
#include <QtWaylandClient/private/qwaylandwindow_p.h>
using namespace LayerShellQt;
class LayerShellQt::WindowPrivate
@ -33,11 +32,8 @@ public:
Window::KeyboardInteractivity keyboardInteractivity = Window::KeyboardInteractivityOnDemand;
Window::Layer layer = Window::LayerTop;
QMargins margins;
QSize desiredSize = QSize(0, 0);
QPointer<QScreen> screen;
bool wantsToBeOnActiveScreen = false;
Window::ScreenConfiguration screenConfiguration = Window::ScreenFromQWindow;
bool closeOnDismissed = true;
bool activateOnShow = true;
};
static QMap<QWindow *, Window *> s_map;
@ -101,21 +97,6 @@ QMargins Window::margins() const
return d->margins;
}
void Window::setDesiredSize(const QSize &size)
{
if (size == d->desiredSize) {
return;
}
d->desiredSize = size;
Q_EMIT desiredSizeChanged();
}
QSize Window::desiredSize() const
{
return d->desiredSize;
}
void Window::setKeyboardInteractivity(KeyboardInteractivity interactivity)
{
if (d->keyboardInteractivity != interactivity) {
@ -153,73 +134,14 @@ Window::Layer Window::layer() const
return d->layer;
}
#if LAYERSHELLQTINTERFACE_BUILD_DEPRECATED_SINCE(6, 6)
Window::ScreenConfiguration Window::screenConfiguration() const
{
if (wantsToBeOnActiveScreen()) {
return ScreenFromCompositor;
} else {
// If an explicit screen is set, it's quite inaccurate but it should be fine.
return ScreenFromQWindow;
}
return d->screenConfiguration;
}
void Window::setScreenConfiguration(ScreenConfiguration screenConfiguration)
void Window::setScreenConfiguration(Window::ScreenConfiguration screenConfiguration)
{
static std::once_flag deprecationFlag;
std::call_once(deprecationFlag, []() {
qWarning() << "LayerShellQt.Window.screenConfiguration is deprecated use screen and wantsToBeOnActiveScreen instead";
});
if (screenConfiguration == ScreenFromCompositor) {
setWantsToBeOnActiveScreen(true);
} else {
setWantsToBeOnActiveScreen(false);
setScreen(nullptr);
}
}
#endif
void Window::setWantsToBeOnActiveScreen(bool set)
{
if (d->wantsToBeOnActiveScreen == set) {
return;
}
d->wantsToBeOnActiveScreen = set;
if (d->wantsToBeOnActiveScreen && d->screen) {
d->screen = nullptr;
Q_EMIT screenChanged();
}
Q_EMIT wantsToBeOnActiveScreenChanged();
}
bool Window::wantsToBeOnActiveScreen() const
{
return d->wantsToBeOnActiveScreen;
}
void Window::setScreen(QScreen *screen)
{
if (d->screen == screen) {
return;
}
d->screen = screen;
if (d->screen && d->wantsToBeOnActiveScreen) {
d->wantsToBeOnActiveScreen = false;
Q_EMIT wantsToBeOnActiveScreenChanged();
}
Q_EMIT screenChanged();
}
QScreen *Window::screen() const
{
return d->screen;
d->screenConfiguration = screenConfiguration;
}
bool Window::closeOnDismissed() const
@ -232,68 +154,32 @@ void Window::setCloseOnDismissed(bool close)
d->closeOnDismissed = close;
}
bool Window::activateOnShow() const
{
return d->activateOnShow;
}
void Window::setActivateOnShow(bool activateOnShow)
{
d->activateOnShow = activateOnShow;
}
Window::Window(QWindow *window)
: QObject(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.";
}
if (window->handle()) {
initializeShell();
}
}
window->create();
bool Window::eventFilter(QObject *watched, QEvent *event)
{
auto window = qobject_cast<QWindow *>(watched);
if (!window) {
return false;
auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle());
if (!waylandWindow) {
qCWarning(LAYERSHELLQT) << window << "is not a wayland window. Not creating zwlr_layer_surface";
return;
}
if (event->type() == QEvent::PlatformSurface) {
if (auto pse = static_cast<QPlatformSurfaceEvent *>(event); pse->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
initializeShell();
}
}
return false;
}
void Window::initializeShell()
{
// FIXME
// auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(d->parentWindow->handle());
// if (!waylandWindow) {
// qCWarning(LAYERSHELLQT) << d->parentWindow << "is not a wayland window. Not creating zwlr_layer_surface";
// return;
// }
static QWaylandLayerShellIntegration *shellIntegration = nullptr;
if (!shellIntegration) {
shellIntegration = new QWaylandLayerShellIntegration();
// Why does it take a display that is unused
if (!shellIntegration->initialize(nullptr)) {
if (!shellIntegration->initialize(waylandWindow->display())) {
delete shellIntegration;
shellIntegration = nullptr;
qCWarning(LAYERSHELLQT) << "Failed to initialize layer-shell integration, possibly because compositor does not support the layer-shell protocol";
return;
}
}
// FIXME this is relevant when the plugin is not the default shell integration
// Something like QWaylandShellIntegration::assignToWindow(QPlatformWindow *)?
// waylandWindow->setShellIntegration(shellIntegration);
waylandWindow->setShellIntegration(shellIntegration);
}
Window *Window::get(QWindow *window)

View File

@ -27,12 +27,7 @@ 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(bool activateOnShow READ activateOnShow WRITE setActivateOnShow)
Q_PROPERTY(bool wantsToBeOnActiveScreen READ wantsToBeOnActiveScreen WRITE setWantsToBeOnActiveScreen NOTIFY wantsToBeOnActiveScreenChanged)
Q_PROPERTY(QScreen *screen READ screen WRITE setScreen NOTIFY screenChanged)
#if LAYERSHELLQTINTERFACE_ENABLE_DEPRECATED_SINCE(6, 6)
Q_PROPERTY(ScreenConfiguration screenConfiguration READ screenConfiguration WRITE setScreenConfiguration)
#endif
public:
~Window() override;
@ -44,8 +39,8 @@ public:
AnchorLeft = 4, ///< The left edge of the anchor rectangle
AnchorRight = 8, ///< The right edge of the anchor rectangle
};
Q_ENUM(Anchor);
Q_DECLARE_FLAGS(Anchors, Anchor)
Q_FLAG(Anchors)
/**
* This enum type is used to specify the layer where a surface can be put in.
@ -68,6 +63,17 @@ 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;
@ -80,57 +86,14 @@ public:
void setMargins(const QMargins &margins);
QMargins margins() const;
void setDesiredSize(const QSize &size);
QSize desiredSize() const;
void setKeyboardInteractivity(KeyboardInteractivity interactivity);
KeyboardInteractivity keyboardInteractivity() const;
void setLayer(Layer layer);
Layer layer() const;
#if LAYERSHELLQTINTERFACE_ENABLE_DEPRECATED_SINCE(6, 6)
/**
* 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.
*/
LAYERSHELLQTINTERFACE_DEPRECATED_VERSION(6, 6, "Use wantsToBeOnActiveScreen and screen instead")
enum ScreenConfiguration {
ScreenFromQWindow = 0,
ScreenFromCompositor = 1,
};
Q_ENUM(ScreenConfiguration)
LAYERSHELLQTINTERFACE_DEPRECATED_VERSION(6, 6, "Use wantsToBeOnActiveScreen and screen instead")
void setScreenConfiguration(ScreenConfiguration screenConfiguration);
LAYERSHELLQTINTERFACE_DEPRECATED_VERSION(6, 6, "Use wantsToBeOnActiveScreen and screen instead")
ScreenConfiguration screenConfiguration() const;
#endif
/**
* 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 the screen() is @c null and the wantsToBeOnActiveScreen() is @c false, then the
* QWindow::screen() will be used to decide what screen the layer shell surface should be placed on.
*
* The screen() will be reset if @a set is @c true.
*/
void setWantsToBeOnActiveScreen(bool set);
bool wantsToBeOnActiveScreen() const;
/**
* Indicates that the layer shell surface should be placed on the specified @a screen.
*
* If the screen() is @c null and the wantsToBeOnActiveScreen() is @c false, then the
* QWindow::screen() will be used to decide what screen the layer shell surface should be placed on.
*
* The wantsToBeOnActiveScreen() will be reset to @c false after calling this function.
*/
void setScreen(QScreen *screen);
QScreen *screen() const;
/**
* Sets a string based identifier for this window.
@ -151,18 +114,6 @@ public:
void setCloseOnDismissed(bool close);
bool closeOnDismissed() const;
/**
* Whether the window should requestActivate on show.
*
* Normally, you want this enabled but in case of e.g. a desktop window, this can be disabled.
*
* It does nothing when KeyboardInteractivity is KeyboardInteractivityNone.
*
* The default is true.
*/
void setActivateOnShow(bool activateOnShow);
bool activateOnShow() const;
/**
* Gets the LayerShell Window for a given Qt Window
* Ownership is not transferred
@ -171,22 +122,15 @@ public:
static Window *qmlAttachedProperties(QObject *object);
bool eventFilter(QObject *watched, QEvent *event) override;
Q_SIGNALS:
void anchorsChanged();
void exclusionZoneChanged();
void exclusiveEdgeChanged();
void marginsChanged();
void desiredSizeChanged();
void keyboardInteractivityChanged();
void layerChanged();
void wantsToBeOnActiveScreenChanged();
void screenChanged();
private:
void initializeShell();
Window(QWindow *window);
QScopedPointer<WindowPrivate> d;
};

View File

@ -9,6 +9,9 @@
#include "qwaylandlayersurface_p.h"
#include "qwaylandxdgactivationv1_p.h"
#include <QtWaylandClient/private/qwaylanddisplay_p.h>
#include <QtWaylandClient/private/qwaylandwindow_p.h>
namespace LayerShellQt
{
QWaylandLayerShellIntegration::QWaylandLayerShellIntegration()
@ -29,4 +32,5 @@ QtWaylandClient::QWaylandShellSurface *QWaylandLayerShellIntegration::createShel
return new QWaylandLayerSurface(this, window);
}
}

View File

@ -18,19 +18,14 @@ class QWaylandXdgActivationV1;
namespace LayerShellQt
{
class LAYERSHELLQT_EXPORT QWaylandLayerShellIntegration : public QtWaylandClient::QWaylandShellIntegrationTemplate<QWaylandLayerShellIntegration>,
public QtWayland::zwlr_layer_shell_v1
class LAYERSHELLQT_EXPORT QWaylandLayerShellIntegration : public QtWaylandClient::QWaylandShellIntegrationTemplate<QWaylandLayerShellIntegration>, public QtWayland::zwlr_layer_shell_v1
{
public:
QWaylandLayerShellIntegration();
~QWaylandLayerShellIntegration() override;
QWaylandXdgActivationV1 *activation() const
{
return m_xdgActivation.data();
}
QWaylandXdgActivationV1 *activation() const { return m_xdgActivation.data(); }
QtWaylandClient::QWaylandShellSurface *createShellSurface(QtWaylandClient::QWaylandWindow *window) override;
private:
QScopedPointer<QWaylandXdgActivationV1> m_xdgActivation;
};

View File

@ -10,10 +10,11 @@
#include "qwaylandlayersurface_p.h"
#include "qwaylandxdgactivationv1_p.h"
#include <QtWaylandClient/private/qwaylandscreen_p.h>
#include <QtWaylandClient/private/qwaylandsurface_p.h>
#include <QtWaylandClient/private/qwaylandwindow_p.h>
#include <QGuiApplication>
#include <QScreen>
#include <qpa/qplatformwindow.h>
#include <qpa/qplatformwindow_p.h> // private native interface
namespace LayerShellQt
{
@ -21,19 +22,12 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell,
: QtWaylandClient::QWaylandShellSurface(window)
, QtWayland::zwlr_layer_surface_v1()
, m_shell(shell)
// FIXME reverse lookup would a QWindow getter make sense, also below a bunch
// QWindow * QWaylandShellSurface::window
, m_interface(Window::get(platformWindow()->window()))
, m_interface(Window::get(window->window()))
, m_window(window)
{
wl_output *output = nullptr;
if (!m_interface->wantsToBeOnActiveScreen()) {
QScreen *desiredScreen = m_interface->screen();
if (!desiredScreen) {
desiredScreen = platformWindow()->window()->screen();
}
auto waylandScreen = platformWindow()->window()->screen()->nativeInterface<QNativeInterface::QWaylandScreen>();
if (m_interface->screenConfiguration() == Window::ScreenFromQWindow) {
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
if (!waylandScreen) {
@ -42,7 +36,7 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell,
output = waylandScreen->output();
}
}
init(shell->get_layer_surface(wlSurface(), output, m_interface->layer(), m_interface->scope()));
init(shell->get_layer_surface(window->waylandSurface()->object(), output, m_interface->layer(), m_interface->scope()));
connect(m_interface, &Window::layerChanged, this, [this]() {
setLayer(m_interface->layer());
});
@ -50,11 +44,7 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell,
setAnchor(m_interface->anchors());
connect(m_interface, &Window::anchorsChanged, this, [this]() {
setAnchor(m_interface->anchors());
if (m_interface->desiredSize().isNull()) {
setDesiredSize(m_lastContentGeometry.size());
} else {
setDesiredSize(m_interface->desiredSize());
}
setDesiredSize(m_window->windowContentGeometry().size());
});
setExclusiveZone(m_interface->exclusionZone());
@ -71,51 +61,44 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell,
setMargins(m_interface->margins());
});
connect(m_interface, &Window::desiredSizeChanged, this, [this]() {
if (!m_interface->desiredSize().isNull()) {
setDesiredSize(m_interface->desiredSize());
}
});
setKeyboardInteractivity(m_interface->keyboardInteractivity());
connect(m_interface, &Window::keyboardInteractivityChanged, this, [this]() {
setKeyboardInteractivity(m_interface->keyboardInteractivity());
});
if (m_interface->desiredSize().isNull()) {
// FIXME QWaylandWindow should call setWindowContentGeometry when creating the shell surface
// / Does a setWindowContentGeometry call arrive early enough?
// setDesiredSize(window->windowContentGeometry().size());
} else {
setDesiredSize(m_interface->desiredSize());
}
}
void QWaylandLayerSurface::setContentGeometry(const QRect &rect)
{
m_lastContentGeometry = rect;
if (m_interface->desiredSize().isNull()) {
setDesiredSize(rect.size());
}
setDesiredSize(window->windowContentGeometry().size());
requestWaylandSync();
}
QWaylandLayerSurface::~QWaylandLayerSurface()
{
for (auto syncCallback: m_waitForSyncCallbacks) {
wl_callback_destroy(syncCallback);
}
m_waitForSyncCallbacks.clear();
destroy();
}
void QWaylandLayerSurface::zwlr_layer_surface_v1_closed()
{
if (m_interface->closeOnDismissed()) {
platformWindow()->close();
window()->window()->close();
}
}
void QWaylandLayerSurface::zwlr_layer_surface_v1_configure(uint32_t serial, uint32_t width, uint32_t height)
{
qDebug() << "got configure";
ack_configure(serial);
m_pendingSize = QSize(width, height);
// because configure lacks a way to match it up with our resize requests
// we apply it only when our callback sync is complete
if (!m_waitForSyncCallbacks.isEmpty()) {
m_hasPendingConfigureToApply = true;
return;
}
if (!m_configured) {
m_configured = true;
applyConfigure();
@ -123,8 +106,10 @@ void QWaylandLayerSurface::zwlr_layer_surface_v1_configure(uint32_t serial, uint
} else {
// Later configures are resizes, so we have to queue them up for a time when we
// are not painting to the window.
applyConfigureWhenPossible();
window()->applyConfigureWhenPossible();
}
qDebug() << "end got confiure";
}
void QWaylandLayerSurface::attachPopup(QtWaylandClient::QWaylandShellSurface *popup)
@ -140,7 +125,10 @@ void QWaylandLayerSurface::attachPopup(QtWaylandClient::QWaylandShellSurface *po
void QWaylandLayerSurface::applyConfigure()
{
resizeFromApplyConfigure(m_pendingSize);
m_configuring = true;
qDebug() << "resizing panel to " << m_pendingSize;
window()->resizeFromApplyConfigure(m_pendingSize);
m_configuring = false;
}
void QWaylandLayerSurface::setDesiredSize(const QSize &size)
@ -155,6 +143,7 @@ void QWaylandLayerSurface::setDesiredSize(const QSize &size)
if (verticallyConstrained) {
effectiveSize.setHeight(0);
}
qDebug() << "requesting size " << effectiveSize;
set_size(effectiveSize.width(), effectiveSize.height());
}
@ -191,11 +180,14 @@ void QWaylandLayerSurface::setLayer(uint32_t layer)
set_layer(layer);
}
void QWaylandLayerSurface::setWindowSize(const QSize &size)
void QWaylandLayerSurface::setWindowGeometry(const QRect &geometry)
{
if (m_interface->desiredSize().isNull()) {
setDesiredSize(size);
if (m_configuring) {
return;
}
setDesiredSize(geometry.size());
requestWaylandSync();
}
bool QWaylandLayerSurface::requestActivate()
@ -205,22 +197,19 @@ bool QWaylandLayerSurface::requestActivate()
return false;
}
if (!m_activationToken.isEmpty()) {
activation->activate(m_activationToken, wlSurface());
activation->activate(m_activationToken, window()->wlSurface());
m_activationToken = {};
return true;
} else if (const auto token = qEnvironmentVariable("XDG_ACTIVATION_TOKEN"); !token.isEmpty()) {
activation->activate(token, wlSurface());
qunsetenv("XDG_ACTIVATION_TOKEN");
return true;
} else {
const auto focusWindow = QGuiApplication::focusWindow();
const auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
const auto surface = focusWindow ? focusWindow->nativeInterface<QNativeInterface::Private::QWaylandWindow>()->surface() : wlSurface();
if (const auto seat = waylandApp->lastInputSeat()) {
const auto tokenProvider = activation->requestXdgActivationToken(seat, surface, waylandApp->lastInputSerial(), QString());
connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, this, [this](const QString &token) {
m_shell->activation()->activate(token, wlSurface());
});
const auto wlWindow = focusWindow ? static_cast<QtWaylandClient::QWaylandWindow*>(focusWindow->handle()) : window();
if (const auto seat = wlWindow->display()->lastInputDevice()) {
const auto tokenProvider = activation->requestXdgActivationToken(
wlWindow->display(), wlWindow->wlSurface(), 0, QString());
connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, this,
[this](const QString &token) {
m_shell->activation()->activate(token, window()->wlSurface());
});
connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, tokenProvider, &QObject::deleteLater);
return true;
}
@ -228,23 +217,6 @@ bool QWaylandLayerSurface::requestActivate()
return false;
}
bool QWaylandLayerSurface::requestActivateOnShow()
{
if (!m_interface->activateOnShow()) {
return false;
}
if (m_interface->keyboardInteractivity() == Window::KeyboardInteractivityNone) {
return false;
}
if (platformWindow()->window()->property("_q_showWithoutActivating").toBool()) {
return false;
}
return requestActivate();
}
void QWaylandLayerSurface::setXdgActivationToken(const QString &token)
{
m_activationToken = token;
@ -256,21 +228,63 @@ void QWaylandLayerSurface::requestXdgActivationToken(quint32 serial)
if (!activation->isActive()) {
return;
}
const auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
auto tokenProvider = activation->requestXdgActivationToken(waylandApp->lastInputSeat(), wlSurface(), serial, QString());
auto tokenProvider = activation->requestXdgActivationToken(
window()->display(), window()->wlSurface(), serial, QString());
connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, this, [this](const QString &token) {
// FIXME No way to do this without knowing QWaylandWindow because QPlatofrmWindow is not QObject
// But we wanted to make a better API as well or alternatively the signal needs to be on QWaylandShellSurface as well
// Q_EMIT window()->xdgActivationTokenCreated(token);
// QMetaObject::invokeMethod(window(), "xdgActivationTokenCreated", token);
});
connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, this,
[this](const QString &token) {
Q_EMIT window()->xdgActivationTokenCreated(token);
});
connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, tokenProvider, &QObject::deleteLater);
}
const wl_callback_listener QWaylandLayerSurface::syncCallbackListener = {
.done = [](void *data, struct wl_callback *callback, uint32_t time){
Q_UNUSED(time);
wl_callback_destroy(callback);
QWaylandLayerSurface *layerSurface = static_cast<QWaylandLayerSurface *>(data);
layerSurface->m_waitForSyncCallbacks.removeOne(callback);
layerSurface->handleWaylandSyncDone();
}
};
void QWaylandLayerSurface::requestWaylandSync()
{
auto syncCallback = wl_display_sync(m_window->display()->wl_display());
m_waitForSyncCallbacks.append(syncCallback);
wl_callback_add_listener(syncCallback, &syncCallbackListener, this);
}
void QWaylandLayerSurface::handleWaylandSyncDone()
{
qDebug() << "handle sync done";
if (!window()->window()->isVisible() || !m_waitForSyncCallbacks.isEmpty()) {
return;
}
if (m_hasPendingConfigureToApply) {
m_hasPendingConfigureToApply = false;
if (!m_configured) {
m_configured = true;
applyConfigure();
sendExpose();
} else {
// Later configures are resizes, so we have to queue them up for a time when we
// are not painting to the window.
window()->applyConfigureWhenPossible();
}
}
qDebug() << "end handle sync done";
}
void QWaylandLayerSurface::sendExpose()
{
// FIXME No way to call this, needs wrapper in QWaylandShellSurface
// window()->updateExposure();
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
window()->handleExpose(QRect(QPoint(), m_pendingSize));
#else
window()->sendRecursiveExposeEvent();
#endif
}
}

View File

@ -8,7 +8,6 @@
#ifndef _LAYERSURFACE_H
#define _LAYERSURFACE_H
#include <QRect>
#include <wayland-client.h>
#include "qwaylandlayershellintegration_p.h"
@ -31,7 +30,7 @@ public:
bool isExposed() const override
{
return m_configured;
return m_configured && m_waitForSyncCallbacks.isEmpty();
}
void attachPopup(QtWaylandClient::QWaylandShellSurface *popup) override;
@ -42,16 +41,17 @@ public:
void setMargins(const QMargins &margins);
void setKeyboardInteractivity(uint32_t interactivity);
void setLayer(uint32_t layer);
void setContentGeometry(const QRect &rect) override;
void applyConfigure() override;
void setWindowSize(const QSize &size) override;
void setWindowGeometry(const QRect &geometry) override;
bool requestActivate() override;
bool requestActivateOnShow() override;
void setXdgActivationToken(const QString &token) override;
void requestXdgActivationToken(quint32 serial) override;
private:
void requestWaylandSync();
void handleWaylandSyncDone();
void sendExpose();
void zwlr_layer_surface_v1_configure(uint32_t serial, uint32_t width, uint32_t height) override;
void zwlr_layer_surface_v1_closed() override;
@ -61,11 +61,14 @@ private:
QtWaylandClient::QWaylandWindow *m_window;
QSize m_pendingSize;
QString m_activationToken;
QRect m_lastContentGeometry;
bool m_configured = false;
};
bool m_configuring = false;
bool m_hasPendingConfigureToApply = false;
static const wl_callback_listener syncCallbackListener;
QList<struct wl_callback*> m_waitForSyncCallbacks;
};
}
#endif

View File

@ -3,6 +3,8 @@
*/
#include "qwaylandxdgactivationv1_p.h"
#include <QtWaylandClient/private/qwaylanddisplay_p.h>
#include <QtWaylandClient/private/qwaylandinputdevice_p.h>
QWaylandXdgActivationV1::QWaylandXdgActivationV1()
: QWaylandClientExtensionTemplate<QWaylandXdgActivationV1>(1)
@ -17,8 +19,10 @@ QWaylandXdgActivationV1::~QWaylandXdgActivationV1()
}
}
QWaylandXdgActivationTokenV1 *
QWaylandXdgActivationV1::requestXdgActivationToken(::wl_seat *seat, struct ::wl_surface *surface, std::optional<uint32_t> serial, const QString &app_id)
QWaylandXdgActivationTokenV1 *QWaylandXdgActivationV1::requestXdgActivationToken(QtWaylandClient::QWaylandDisplay *display,
struct ::wl_surface *surface,
std::optional<uint32_t> serial,
const QString &app_id)
{
auto wl = get_activation_token();
auto provider = new QWaylandXdgActivationTokenV1;
@ -30,8 +34,8 @@ QWaylandXdgActivationV1::requestXdgActivationToken(::wl_seat *seat, struct ::wl_
if (!app_id.isEmpty()) {
provider->set_app_id(app_id);
}
if (serial && seat) {
provider->set_serial(*serial, seat);
if (serial && display->lastInputDevice()) {
provider->set_serial(*serial, display->lastInputDevice()->wl_seat());
}
provider->commit();
return provider;

View File

@ -42,7 +42,7 @@ public:
~QWaylandXdgActivationV1() override;
QWaylandXdgActivationTokenV1 *
requestXdgActivationToken(::wl_seat *seat, struct ::wl_surface *surface, std::optional<uint32_t> serial, const QString &app_id);
requestXdgActivationToken(QtWaylandClient::QWaylandDisplay *display, struct ::wl_surface *surface, std::optional<uint32_t> serial, const QString &app_id);
};
#endif // QWAYLANDXDGACTIVATIONV1_P_H

View File

@ -1,42 +0,0 @@
/*
* 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
}
}

View File

@ -53,7 +53,7 @@ int main(int argc, char **argv)
QGuiApplication app(argc, argv);
const auto layerMetaEnum = QMetaEnum::fromType<Window::Layer>();
const auto anchorMetaEnum = QMetaEnum::fromType<Window::Anchors>();
const auto anchorMetaEnum = QMetaEnum::fromType<Window::Anchor>();
QCommandLineParser parser;
QCommandLineOption marginsOption(QStringLiteral("margins"), QStringLiteral("Window margins"), QStringLiteral("pixels"), QStringLiteral("0"));

View File

@ -39,7 +39,6 @@ Item
LayerShell.Window.anchors: LayerShell.Window.AnchorLeft
LayerShell.Window.layer: LayerShell.Window.LayerTop
LayerShell.Window.exclusionZone: width
LayerShell.Window.margins.left: 100
width: 100
height: 100