Compare commits

...

48 Commits

Author SHA1 Message Date
aeb8d198ce update version for new release 2024-03-26 14:23:48 +00:00
f32b64244f update version for new release 2024-03-05 23:23:45 +00:00
9fb17e63b2 update version for new release 2024-03-05 22:17:10 +00:00
ec1a2d02fb Update Frameworks version requirement to 6.0.0
GIT_SILENT
2024-02-21 14:43:27 +00:00
c902d638ec Port to asynchronous roundtrip
When a resize is driven client side we wait for the compositor to have a
chance to reconfigure us before submitting the next frame.

Using a blocking round trip caused an issue. Instead block isExposed and
trigger an expose event whilst a sync is in progress.
2024-02-16 17:15:50 +00:00
176b7648d4 Synchronise client driven resizing
Qt's resizing is inherently synchronous. When an application calls
QWindow::setGeometry with a new size, we expect it to make that resize.
An expose event of the requested size will be generated.

Wayland is by default async, a client requests a size, and then will be
configured to that size, or potentially another size.

The simplest way to map the two APIs is to roundtrip when the client
wants to resize. This way we can guarantee that a call to
`setGeometry();  update()` will have the server configured size before
the paint.

In practice it's still not perfect due to other issues, but at least
will sort itself out within a frame.


(cherry picked from commit 697c747c58)
2024-02-14 12:10:19 +00:00
cce9896e42 Fix build with Qt 6.7
This also fixes a hypothetical case of a layer shell with subsurfaces
and matches the call made by xdgshell after the first configure.


(cherry picked from commit 6427176da4)
2024-02-08 10:19:13 +00:00
f7fee6e7f6 update version for new release 2024-02-01 09:46:00 +00:00
66ec181e1e Update version number for 5.93.0
GIT_SILENT
2024-01-31 13:01:20 +00:00
1abc1f381f Add a new setExclusiveEdge call in the protocol
This can be used to disambiguate the exclusive edge when the anchors are
on a corner (so there  would be 2 candidates)

it's quite quick and dirty mostly to understand if we do want to push
for something along the lines


(cherry picked from commit ad5246f0d0)
2024-01-26 11:39:06 +00:00
47539db4b3 Update version number for 5.92.90
GIT_SILENT
2024-01-10 14:17:49 +00:00
078f36f8f3 Update version number for 5.92.0
GIT_SILENT
2024-01-10 12:29:03 +00:00
7d3194034c Use ECM QML module so the module can be used outside of repository 2024-01-04 14:42:17 -05:00
e3098a660a Fix constrained check
The value in parantheses was always zero.
2023-12-21 13:58:24 +00:00
d379bc8d8e Update version number for 5.91.90
GIT_SILENT
2023-12-20 18:54:50 +00:00
715e629dd8 Update version number for 5.91.0
GIT_SILENT
2023-12-20 12:02:54 +00:00
8ae3b0aef8 Qt6 check code as apps is qt6 only. 2023-12-15 21:44:24 +01:00
6f0bca5593 Revert "Add a (temporary) way to attach popups to layer surfaces"
This reverts commit fdab1544fb.

Qt 6.6 is out with all the necessary multi-shell apis.
2023-12-08 11:55:52 +00:00
d1ab27dd53 Update Qt version requirement to 6.6.0
GIT_SILENT
2023-12-08 12:10:27 +01:00
4569e78e25 Update version number for 5.90.90
GIT_SILENT
2023-12-07 16:41:03 +00:00
ddb0490592 Change default keyboard interactivity to OnDemand
OnDemand is a better default as it still ensures that the window will
receive keyboard input by default, but it's less aggressive.
2023-11-30 10:06:18 +00:00
3c116e7550 Revert "Change default keyboard interactivity from exclusive to none"
This reverts commit be63783888.

It broke keyboard input in sddm greeter. While defaulting to a non-spec
value is not great, it's also not that critical. It might be worth
considering synchronizing Qt::WindowDoesNotAcceptFocus with the keyboard
interactivity flag, but the tricky part is that the keyboard interactivty
is not just a boolean.

BUG: 477251
2023-11-30 10:06:18 +00:00
9e1c5357ac Update version number for 5.90.0
GIT_SILENT
2023-11-29 10:24:25 +00:00
76e8f33cbc GIT_SILENT: Port to new CI template 2023-11-19 16:20:06 +01:00
25bc38dac1 Update version number for 5.81.80
GIT_SILENT
2023-11-17 12:28:29 +00:00
aa167e8da5 Update version number for 5.81.0
GIT_SILENT
2023-11-09 12:31:25 +00:00
de9fdb3fb7 Support XDG activation
QWaylandLayerShellIntegration has virtual hooks for Xdg Activation.

This is important to hook up in layer shell because activation using
requestActivate in Qt will go through this path. It also means we have
support for us to drop the implementation in KWindowSystem in favour of
calling into Qt.
2023-11-06 12:41:20 +00:00
00b26a196d set ecm soversion to project major
again probably not used but feels best to make them match
2023-11-03 10:19:42 +00:00
fcaf8cf95d Update PROJECT_VERSION_MAJOR to 6, this is mostly used in some internal soversions 2023-11-01 19:22:21 +00:00
1573a89aac Skip empty parts for stringToEnum template
metaEnum.keyToValue("") will results all flags get set, thus creating a
enum with empty (0) flags become impossible. This patch fixes that.
2023-09-26 17:25:20 +08:00
721c0ae334 Remove an inappropriate log message 2023-09-06 14:52:55 +03:00
143fd1755a Expose the Window interface to QML
If we are designing our UI's windows from QML, it makes sense that we
might want to configure how they're placed from the same place.

Everything was already in place but for a few technical bits which this
change adds.

Signed-off-by: Victoria Fischer <victoria.fischer@mbition.io>
2023-07-13 11:23:01 +02:00
d6aeaef1dc We use Qt6/kf6 only 2023-06-26 20:42:27 +02:00
c97826ab18 It compiles fine without deprecated methods 2023-06-12 14:00:48 +02:00
3bfed84c45 Now we depend against qt6 2023-06-08 19:17:22 +02:00
4cb2a27a4c Use qt6 only 2023-06-04 09:17:37 +02:00
9750fd8be7 Avoid emitting excessive changed signals
If the layer shell surface properties don't change, don't emit the
changed signals and issue wayland requests.
2023-05-17 14:57:09 +03:00
5e88f449b0 Introduce Window::closeOnDismissed() flag
This can be used to control the sending of QEvent::Close event.
2023-05-16 17:58:48 +03:00
497d50c4df Emit Window::layerChanged signal
Otherwise it's not possible to change layers at runtime.
2023-05-13 12:18:12 +03:00
541497ff5f Add wayland to third-party deps 2023-05-05 10:22:39 +02:00
1358743441 Call LayerShell::set_size when the window geometry changes
Without this when krunner expands, the frame after kwin will resize it
back to the last size the layer shell requested to be resized to.

To detect when we should not send an explicit size on window resize
events we check the anchors; if we're constrained on opposing edges we
want the compositor alone to set the size.
2023-05-01 14:38:36 +00:00
be63783888 Change default keyboard interactivity from exclusive to none
To be in line with the default of the protocol
2023-04-27 12:28:02 +02:00
87753746b9 chore: add missing override in example 2023-04-24 23:23:09 +03:00
49f31bb22d Replace Window::desiredOutput with a Window::screenConfiguration enum
To not overlap with QWindow::screen too much, introduce an enum to make it
more clear what the options and the resulting behaviour are.
2023-04-24 20:11:13 +00:00
511b92f4ab Port to QWaylandShellSurface::attachPopup() 2023-04-22 12:11:04 +03:00
fdab1544fb Add a (temporary) way to attach popups to layer surfaces
The new API is temporary. It's needed to help us with porting plasma to
the layer shell protocol until Qt 6.6 is released, which includes
QWaylandShellSurface::attachPopup().
2023-04-22 12:11:04 +03:00
2985398375 Port to QWaylandWindow::setShellIntegration()
With QWaylandWindow::setShellIntegration(), it's possible to use
xdg-shell and layer-shell protocols in the same process. It's important
for plasmashell, where we want to use the layer shell protocol for
special surfaces such as the desktop background, and the xdg shell
protocol for dialogs.

In order to make a QWindow use the layer shell protocol, you need to
call LayerShellQt::Window::get() before the window is mapped.
2023-04-22 12:11:01 +03:00
07e63cab8c Require C++20 2023-04-20 08:45:39 +02:00
20 changed files with 625 additions and 184 deletions

View File

@ -2,6 +2,8 @@
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
include: include:
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/reuse-lint.yml - project: sysadmin/ci-utilities
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml file:
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd-qt6.yml - /gitlab-templates/reuse-lint.yml
- /gitlab-templates/linux-qt6.yml
- /gitlab-templates/freebsd-qt6.yml

View File

@ -5,3 +5,5 @@ Dependencies:
- 'on': ['@all'] - 'on': ['@all']
'require': 'require':
'frameworks/extra-cmake-modules': '@latest-kf6' 'frameworks/extra-cmake-modules': '@latest-kf6'
'third-party/wayland': '@latest'
'third-party/wayland-protocols': '@latest'

View File

@ -4,16 +4,16 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(layershellqt) project(layershellqt)
set(PROJECT_VERSION "5.27.80") set(PROJECT_VERSION "6.0.3")
set(PROJECT_VERSION_MAJOR 5) set(PROJECT_VERSION_MAJOR 6)
set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD 99)
set(QT_MIN_VERSION "6.4.0") set(QT_MIN_VERSION "6.6.0")
set(KF6_MIN_VERSION "5.240.0") set(KF6_MIN_VERSION "6.0.0")
set(KDE_COMPILERSETTINGS_LEVEL "5.82") set(KDE_COMPILERSETTINGS_LEVEL "5.82")
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(ECM ${KF6_MIN_VERSION} REQUIRED NO_MODULE) find_package(ECM ${KF6_MIN_VERSION} REQUIRED NO_MODULE)
@ -30,13 +30,10 @@ include(FeatureSummary)
include(GenerateExportHeader) include(GenerateExportHeader)
include(KDEClangFormat) include(KDEClangFormat)
include(ECMQtDeclareLoggingCategory) include(ECMQtDeclareLoggingCategory)
include(ECMQmlModule)
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS WaylandClient Qml) find_package(Qt6 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS WaylandClient Qml)
if (QT_MAJOR_VERSION EQUAL "5")
find_package(Qt5XkbCommonSupport REQUIRED PRIVATE)
find_package(QtWaylandScanner REQUIRED)
endif()
find_package(WaylandScanner REQUIRED) find_package(WaylandScanner REQUIRED)
find_package(Wayland 1.3 COMPONENTS Client Server) find_package(Wayland 1.3 COMPONENTS Client Server)
find_package(WaylandProtocols REQUIRED) find_package(WaylandProtocols REQUIRED)
@ -51,13 +48,13 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu90")
ecm_setup_version(${PROJECT_VERSION} VARIABLE_PREFIX LAYERSHELLQT ecm_setup_version(${PROJECT_VERSION} VARIABLE_PREFIX LAYERSHELLQT
VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/layershellqt_version.h" VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/layershellqt_version.h"
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/LayerShellQtConfigVersion.cmake" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/LayerShellQtConfigVersion.cmake"
SOVERSION 5) SOVERSION ${PROJECT_VERSION_MAJOR})
file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h) file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h)
kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES}) kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
ecm_set_disabled_deprecation_versions(QT 5.15.2 ecm_set_disabled_deprecation_versions(QT 6.5
KF 5.101 KF 5.240
) )
add_subdirectory(src) add_subdirectory(src)

View File

@ -4,6 +4,6 @@
@PACKAGE_INIT@ @PACKAGE_INIT@
include(CMakeFindDependencyMacro) include(CMakeFindDependencyMacro)
find_dependency(Qt@QT_MAJOR_VERSION@Gui "@QT_MIN_VERSION@") find_dependency(Qt6Gui "@QT_MIN_VERSION@")
include("${CMAKE_CURRENT_LIST_DIR}/LayerShellQtTargets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/LayerShellQtTargets.cmake")

View File

@ -4,17 +4,13 @@
remove_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS) remove_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS)
add_library(LayerShellQtInterface) add_library(LayerShellQtInterface)
if (QT_MAJOR_VERSION EQUAL "5") qt6_generate_wayland_protocol_client_sources(LayerShellQtInterface FILES
ecm_add_qtwayland_client_protocol(LAYER_SHELL_SOURCES PROTOCOL ${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml BASENAME xdg-shell) ${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml
ecm_add_qtwayland_client_protocol(LAYER_SHELL_SOURCES PROTOCOL wlr-layer-shell-unstable-v1.xml BASENAME wlr-layer-shell-unstable-v1) ${WaylandProtocols_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml
else() ${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
${CMAKE_CURRENT_SOURCE_DIR}/wlr-layer-shell-unstable-v1.xml
)
endif()
ecm_qt_declare_logging_category(LAYER_SHELL_SOURCES ecm_qt_declare_logging_category(LayerShellQtInterface
HEADER HEADER
layershellqt_logging.h layershellqt_logging.h
IDENTIFIER IDENTIFIER
@ -23,8 +19,16 @@ ecm_qt_declare_logging_category(LAYER_SHELL_SOURCES
layershellqt layershellqt
) )
target_sources(LayerShellQtInterface PRIVATE qwaylandlayersurface.cpp interfaces/window.cpp interfaces/shell.cpp qwaylandlayershellintegration.cpp qwaylandlayershell.cpp ${LAYER_SHELL_SOURCES}) target_sources(LayerShellQtInterface PRIVATE
target_link_libraries(LayerShellQtInterface PRIVATE Qt::Gui Qt::WaylandClientPrivate Wayland::Client PkgConfig::XKBCOMMON) qwaylandxdgactivationv1.cpp
qwaylandlayersurface.cpp
qwaylandlayershellintegration.cpp
interfaces/window.cpp
interfaces/shell.cpp
)
target_link_libraries(LayerShellQtInterface PUBLIC Qt::Gui)
target_link_libraries(LayerShellQtInterface PRIVATE Qt::WaylandClientPrivate Wayland::Client PkgConfig::XKBCOMMON)
if (TARGET Qt::XkbCommonSupportPrivate) if (TARGET Qt::XkbCommonSupportPrivate)
target_link_libraries(LayerShellQtInterface PRIVATE Qt::XkbCommonSupportPrivate) target_link_libraries(LayerShellQtInterface PRIVATE Qt::XkbCommonSupportPrivate)
endif() endif()
@ -68,3 +72,5 @@ install(FILES
${CMAKE_CURRENT_BINARY_DIR}/LayerShellQt/layershellqt_export.h ${CMAKE_CURRENT_BINARY_DIR}/LayerShellQt/layershellqt_export.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/LayerShellQt COMPONENT Devel DESTINATION ${KDE_INSTALL_INCLUDEDIR}/LayerShellQt COMPONENT Devel
) )
add_subdirectory(declarative)

View File

@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleix.pol_gonzalez@mercedes-benz.com>
# SPDX-License-Identifier: BSD-3-Clause
ecm_add_qml_module(LayerShellQtQml
URI "org.kde.layershell"
VERSION 1.0
SOURCES layershellqtplugin.cpp)
target_link_libraries(LayerShellQtQml PRIVATE Qt::Qml LayerShellQtInterface)
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-3.0-or-later
*/
#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

@ -5,11 +5,15 @@
*/ */
#include "window.h" #include "window.h"
#include "../qwaylandlayershellintegration_p.h"
#include <layershellqt_logging.h> #include <layershellqt_logging.h>
#include <QPointer> #include <QPointer>
#include <optional> #include <optional>
#include <QtWaylandClient/private/qwaylandwindow_p.h>
using namespace LayerShellQt; using namespace LayerShellQt;
class LayerShellQt::WindowPrivate class LayerShellQt::WindowPrivate
@ -24,10 +28,12 @@ public:
QString scope = QStringLiteral("window"); QString scope = QStringLiteral("window");
Window::Anchors anchors = {Window::AnchorTop | Window::AnchorBottom | Window::AnchorLeft | Window::AnchorRight}; Window::Anchors anchors = {Window::AnchorTop | Window::AnchorBottom | Window::AnchorLeft | Window::AnchorRight};
int32_t exclusionZone = 0; int32_t exclusionZone = 0;
Window::KeyboardInteractivity keyboardInteractivity = Window::KeyboardInteractivityExclusive; Window::Anchor exclusiveEdge = Window::AnchorNone;
Window::KeyboardInteractivity keyboardInteractivity = Window::KeyboardInteractivityOnDemand;
Window::Layer layer = Window::LayerTop; Window::Layer layer = Window::LayerTop;
QMargins margins; QMargins margins;
std::optional<QPointer<QScreen>> desiredOutput; Window::ScreenConfiguration screenConfiguration = Window::ScreenFromQWindow;
bool closeOnDismissed = true;
}; };
static QMap<QWindow *, Window *> s_map; static QMap<QWindow *, Window *> s_map;
@ -39,8 +45,10 @@ Window::~Window()
void Window::setAnchors(Anchors anchors) void Window::setAnchors(Anchors anchors)
{ {
d->anchors = anchors; if (d->anchors != anchors) {
Q_EMIT anchorsChanged(); d->anchors = anchors;
Q_EMIT anchorsChanged();
}
} }
Window::Anchors Window::anchors() const Window::Anchors Window::anchors() const
@ -50,8 +58,10 @@ Window::Anchors Window::anchors() const
void Window::setExclusiveZone(int32_t zone) void Window::setExclusiveZone(int32_t zone)
{ {
d->exclusionZone = zone; if (d->exclusionZone != zone) {
Q_EMIT exclusionZoneChanged(); d->exclusionZone = zone;
Q_EMIT exclusionZoneChanged();
}
} }
int32_t Window::exclusionZone() const int32_t Window::exclusionZone() const
@ -59,10 +69,27 @@ int32_t Window::exclusionZone() const
return d->exclusionZone; return d->exclusionZone;
} }
void Window::setExclusiveEdge(Window::Anchor edge)
{
if (d->exclusiveEdge == edge) {
return;
}
d->exclusiveEdge = edge;
Q_EMIT exclusiveEdgeChanged();
}
Window::Anchor Window::exclusiveEdge() const
{
return d->exclusiveEdge;
}
void Window::setMargins(const QMargins &margins) void Window::setMargins(const QMargins &margins)
{ {
d->margins = margins; if (d->margins != margins) {
Q_EMIT marginsChanged(); d->margins = margins;
Q_EMIT marginsChanged();
}
} }
QMargins Window::margins() const QMargins Window::margins() const
@ -72,8 +99,10 @@ QMargins Window::margins() const
void Window::setKeyboardInteractivity(KeyboardInteractivity interactivity) void Window::setKeyboardInteractivity(KeyboardInteractivity interactivity)
{ {
d->keyboardInteractivity = interactivity; if (d->keyboardInteractivity != interactivity) {
Q_EMIT keyboardInteractivityChanged(); d->keyboardInteractivity = interactivity;
Q_EMIT keyboardInteractivityChanged();
}
} }
Window::KeyboardInteractivity Window::keyboardInteractivity() const Window::KeyboardInteractivity Window::keyboardInteractivity() const
@ -83,7 +112,10 @@ Window::KeyboardInteractivity Window::keyboardInteractivity() const
void Window::setLayer(Layer layer) void Window::setLayer(Layer layer)
{ {
d->layer = layer; if (d->layer != layer) {
d->layer = layer;
Q_EMIT layerChanged();
}
} }
void Window::setScope(const QString &scope) void Window::setScope(const QString &scope)
@ -102,19 +134,24 @@ Window::Layer Window::layer() const
return d->layer; return d->layer;
} }
QScreen *Window::desiredOutput() const Window::ScreenConfiguration Window::screenConfiguration() const
{ {
// Don't use .value_or here to avoid a temporary QPointer return d->screenConfiguration;
if (d->desiredOutput.has_value()) {
return d->desiredOutput.value();
}
return d->parentWindow->screen();
} }
void Window::setDesiredOutput(QScreen *output) void Window::setScreenConfiguration(Window::ScreenConfiguration screenConfiguration)
{ {
d->desiredOutput = output; d->screenConfiguration = screenConfiguration;
}
bool Window::closeOnDismissed() const
{
return d->closeOnDismissed;
}
void Window::setCloseOnDismissed(bool close)
{
d->closeOnDismissed = close;
} }
Window::Window(QWindow *window) Window::Window(QWindow *window)
@ -122,13 +159,43 @@ Window::Window(QWindow *window)
, d(new WindowPrivate(window)) , d(new WindowPrivate(window))
{ {
s_map.insert(d->parentWindow, this); s_map.insert(d->parentWindow, this);
window->create();
auto waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle());
if (!waylandWindow) {
qCWarning(LAYERSHELLQT) << window << "is not a wayland window. Not creating zwlr_layer_surface";
return;
}
static QWaylandLayerShellIntegration *shellIntegration = nullptr;
if (!shellIntegration) {
shellIntegration = new QWaylandLayerShellIntegration();
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;
}
}
waylandWindow->setShellIntegration(shellIntegration);
} }
Window *Window::get(QWindow *window) Window *Window::get(QWindow *window)
{ {
if (!window) {
return nullptr;
}
auto layerShellWindow = s_map.value(window); auto layerShellWindow = s_map.value(window);
if (layerShellWindow) { if (layerShellWindow) {
return layerShellWindow; return layerShellWindow;
} }
return new Window(window); return new Window(window);
} }
Window *Window::qmlAttachedProperties(QObject *object)
{
return get(qobject_cast<QWindow *>(object));
}

View File

@ -21,10 +21,19 @@ class WindowPrivate;
class LAYERSHELLQT_EXPORT Window : public QObject class LAYERSHELLQT_EXPORT Window : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(Anchors anchors READ anchors WRITE setAnchors NOTIFY anchorsChanged)
Q_PROPERTY(QString scope READ scope WRITE setScope)
Q_PROPERTY(QMargins margins READ margins WRITE setMargins NOTIFY marginsChanged)
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)
public: public:
~Window() override; ~Window() override;
enum Anchor { enum Anchor {
AnchorNone = 0,
AnchorTop = 1, ///< The top edge of the anchor rectangle AnchorTop = 1, ///< The top edge of the anchor rectangle
AnchorBottom = 2, ///< The bottom edge of the anchor rectangle AnchorBottom = 2, ///< The bottom edge of the anchor rectangle
AnchorLeft = 4, ///< The left edge of the anchor rectangle AnchorLeft = 4, ///< The left edge of the anchor rectangle
@ -54,12 +63,26 @@ public:
}; };
Q_ENUM(KeyboardInteractivity) 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); void setAnchors(Anchors anchor);
Anchors anchors() const; Anchors anchors() const;
void setExclusiveZone(int32_t zone); void setExclusiveZone(int32_t zone);
int32_t exclusionZone() const; int32_t exclusionZone() const;
void setExclusiveEdge(Window::Anchor edge);
Window::Anchor exclusiveEdge() const;
void setMargins(const QMargins &margins); void setMargins(const QMargins &margins);
QMargins margins() const; QMargins margins() const;
@ -69,13 +92,8 @@ public:
void setLayer(Layer layer); void setLayer(Layer layer);
Layer layer() const; Layer layer() const;
/** void setScreenConfiguration(ScreenConfiguration screenConfiguration);
* If set, the compositor will try to put the window on the given screen. ScreenConfiguration screenConfiguration() const;
* If its not set, then the compositor will decide where to put the window.
* Under normal circumstances, this should be the active output.
*/
void setDesiredOutput(QScreen *output);
QScreen *desiredOutput() const;
/** /**
* Sets a string based identifier for this window. * Sets a string based identifier for this window.
@ -87,15 +105,27 @@ public:
void setScope(const QString &scope); void setScope(const QString &scope);
QString scope() const; QString scope() const;
/**
* Whether the QWindow should be closed when the layer surface is dismissed by the compositor.
* For example, if the associated screen has been removed.
*
* This can be used to map the window on another screen.
*/
void setCloseOnDismissed(bool close);
bool closeOnDismissed() const;
/** /**
* Gets the LayerShell Window for a given Qt Window * Gets the LayerShell Window for a given Qt Window
* Ownership is not transferred * Ownership is not transferred
*/ */
static Window *get(QWindow *window); static Window *get(QWindow *window);
static Window *qmlAttachedProperties(QObject *object);
Q_SIGNALS: Q_SIGNALS:
void anchorsChanged(); void anchorsChanged();
void exclusionZoneChanged(); void exclusionZoneChanged();
void exclusiveEdgeChanged();
void marginsChanged(); void marginsChanged();
void keyboardInteractivityChanged(); void keyboardInteractivityChanged();
void layerChanged(); void layerChanged();

View File

@ -1,29 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
* SPDX-FileCopyrightText: 2018 Drew DeVault <sir@cmpwn.com>
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "qwaylandlayershell_p.h"
#include "qwaylandlayersurface_p.h"
namespace LayerShellQt
{
QWaylandLayerShell::QWaylandLayerShell(::wl_registry *registry, uint32_t id, uint32_t version)
: QtWayland::zwlr_layer_shell_v1(registry, id, version)
{
}
QWaylandLayerShell::~QWaylandLayerShell()
{
if (zwlr_layer_shell_v1_get_version(object()) >= ZWLR_LAYER_SHELL_V1_DESTROY_SINCE_VERSION)
zwlr_layer_shell_v1_destroy(object());
}
QWaylandLayerSurface *QWaylandLayerShell::createLayerSurface(QtWaylandClient::QWaylandWindow *window)
{
return new QWaylandLayerSurface(this, window);
}
}

View File

@ -1,32 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
* SPDX-FileCopyrightText: 2018 Drew DeVault <sir@cmpwn.com>
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef _LAYERSHELL_H
#define _LAYERSHELL_H
#include <wayland-client.h>
#include <QtWaylandClient/private/qwaylandshellintegration_p.h>
#include <qwayland-wlr-layer-shell-unstable-v1.h>
#include "qwaylandlayersurface_p.h"
namespace LayerShellQt
{
class LAYERSHELLQT_EXPORT QWaylandLayerShell : public QtWayland::zwlr_layer_shell_v1
{
public:
QWaylandLayerShell(::wl_registry *registry, uint32_t id, uint32_t version);
~QWaylandLayerShell() override;
QWaylandLayerSurface *createLayerSurface(QtWaylandClient::QWaylandWindow *window);
// TODO: Popups
};
}
#endif

View File

@ -5,45 +5,32 @@
* SPDX-License-Identifier: LGPL-3.0-or-later * SPDX-License-Identifier: LGPL-3.0-or-later
*/ */
#include "qwaylandlayershell_p.h"
#include "qwaylandlayershellintegration_p.h" #include "qwaylandlayershellintegration_p.h"
#include "qwaylandlayersurface_p.h"
#include "qwaylandxdgactivationv1_p.h"
#include <QtWaylandClient/private/qwaylanddisplay_p.h> #include <QtWaylandClient/private/qwaylanddisplay_p.h>
#include <QtWaylandClient/private/qwaylandwindow_p.h> #include <QtWaylandClient/private/qwaylandwindow_p.h>
#include <qwayland-wlr-layer-shell-unstable-v1.h>
namespace LayerShellQt namespace LayerShellQt
{ {
QWaylandLayerShellIntegration::QWaylandLayerShellIntegration() QWaylandLayerShellIntegration::QWaylandLayerShellIntegration()
: QWaylandShellIntegrationTemplate<QWaylandLayerShellIntegration>(5)
, m_xdgActivation(new QWaylandXdgActivationV1)
{ {
} }
QWaylandLayerShellIntegration::~QWaylandLayerShellIntegration() QWaylandLayerShellIntegration::~QWaylandLayerShellIntegration()
{ {
} if (object() && zwlr_layer_shell_v1_get_version(object()) >= ZWLR_LAYER_SHELL_V1_DESTROY_SINCE_VERSION) {
zwlr_layer_shell_v1_destroy(object());
bool QWaylandLayerShellIntegration::initialize(QtWaylandClient::QWaylandDisplay *display) }
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QWaylandShellIntegration::initialize(display);
#endif
display->addRegistryListener(registryLayer, this);
return m_layerShell != nullptr;
} }
QtWaylandClient::QWaylandShellSurface *QWaylandLayerShellIntegration::createShellSurface(QtWaylandClient::QWaylandWindow *window) QtWaylandClient::QWaylandShellSurface *QWaylandLayerShellIntegration::createShellSurface(QtWaylandClient::QWaylandWindow *window)
{ {
return m_layerShell->createLayerSurface(window); return new QWaylandLayerSurface(this, window);
} }
void QWaylandLayerShellIntegration::registryLayer(void *data, struct wl_registry *registry, uint32_t id, const QString &interface, uint32_t version)
{
QWaylandLayerShellIntegration *shell = static_cast<QWaylandLayerShellIntegration *>(data);
if (interface == zwlr_layer_shell_v1_interface.name)
shell->m_layerShell.reset(new QWaylandLayerShell(registry, id, std::min(version, 4u)));
}
} }
//#include "qwaylandlayershellintegration.moc"

View File

@ -8,28 +8,26 @@
#ifndef _LAYERSHELLINTEGRATION_P_H #ifndef _LAYERSHELLINTEGRATION_P_H
#define _LAYERSHELLINTEGRATION_P_H #define _LAYERSHELLINTEGRATION_P_H
#include <wayland-client.h>
#include "layershellqt_export.h" #include "layershellqt_export.h"
#include <QtWaylandClient/private/qwaylandshellintegration_p.h> #include <QtWaylandClient/private/qwaylandshellintegration_p.h>
#include <qwayland-wlr-layer-shell-unstable-v1.h>
class QWaylandXdgActivationV1;
namespace LayerShellQt namespace LayerShellQt
{ {
class QWaylandLayerShell;
class LAYERSHELLQT_EXPORT QWaylandLayerShellIntegration : public QtWaylandClient::QWaylandShellIntegration class LAYERSHELLQT_EXPORT QWaylandLayerShellIntegration : public QtWaylandClient::QWaylandShellIntegrationTemplate<QWaylandLayerShellIntegration>, public QtWayland::zwlr_layer_shell_v1
{ {
public: public:
QWaylandLayerShellIntegration(); QWaylandLayerShellIntegration();
~QWaylandLayerShellIntegration() override; ~QWaylandLayerShellIntegration() override;
bool initialize(QtWaylandClient::QWaylandDisplay *display) override; QWaylandXdgActivationV1 *activation() const { return m_xdgActivation.data(); }
QtWaylandClient::QWaylandShellSurface *createShellSurface(QtWaylandClient::QWaylandWindow *window) override; QtWaylandClient::QWaylandShellSurface *createShellSurface(QtWaylandClient::QWaylandWindow *window) override;
private: private:
static void registryLayer(void *data, struct wl_registry *registry, uint32_t id, const QString &interface, uint32_t version); QScopedPointer<QWaylandXdgActivationV1> m_xdgActivation;
QScopedPointer<QWaylandLayerShell> m_layerShell;
}; };
} }

View File

@ -5,28 +5,29 @@
* SPDX-License-Identifier: LGPL-3.0-or-later * SPDX-License-Identifier: LGPL-3.0-or-later
*/ */
#include "interfaces/shell.h" #include "interfaces/window.h"
#include "layershellqt_logging.h" #include "layershellqt_logging.h"
#include "qwaylandlayershell_p.h"
#include "qwaylandlayersurface_p.h" #include "qwaylandlayersurface_p.h"
#include "qwaylandxdgactivationv1_p.h"
#include <QtWaylandClient/private/qwaylandscreen_p.h> #include <QtWaylandClient/private/qwaylandscreen_p.h>
#include <QtWaylandClient/private/qwaylandsurface_p.h> #include <QtWaylandClient/private/qwaylandsurface_p.h>
#include <QtWaylandClient/private/qwaylandwindow_p.h> #include <QtWaylandClient/private/qwaylandwindow_p.h>
#include <QGuiApplication>
namespace LayerShellQt namespace LayerShellQt
{ {
QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShell *shell, QtWaylandClient::QWaylandWindow *window) QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell, QtWaylandClient::QWaylandWindow *window)
: QtWaylandClient::QWaylandShellSurface(window) : QtWaylandClient::QWaylandShellSurface(window)
, QtWayland::zwlr_layer_surface_v1() , QtWayland::zwlr_layer_surface_v1()
, m_shell(shell)
, m_interface(Window::get(window->window()))
, m_window(window)
{ {
LayerShellQt::Window *interface = Window::get(window->window());
Q_ASSERT(interface);
wl_output *output = nullptr; wl_output *output = nullptr;
QScreen *screen = interface->desiredOutput(); if (m_interface->screenConfiguration() == Window::ScreenFromQWindow) {
if (screen) { auto waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen *>(window->window()->screen()->handle());
auto waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen *>(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 // 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 // this will not cast to a QWaylandScreen
if (!waylandScreen) { if (!waylandScreen) {
@ -35,32 +36,36 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShell *shell, QtWaylandC
output = waylandScreen->output(); output = waylandScreen->output();
} }
} }
init(shell->get_layer_surface(window->waylandSurface()->object(), output, interface->layer(), interface->scope())); init(shell->get_layer_surface(window->waylandSurface()->object(), output, m_interface->layer(), m_interface->scope()));
connect(interface, &Window::layerChanged, this, [this, interface]() { connect(m_interface, &Window::layerChanged, this, [this]() {
setLayer(interface->layer()); setLayer(m_interface->layer());
}); });
set_anchor(interface->anchors()); set_anchor(m_interface->anchors());
connect(interface, &Window::anchorsChanged, this, [this, interface]() { connect(m_interface, &Window::anchorsChanged, this, [this]() {
set_anchor(interface->anchors()); set_anchor(m_interface->anchors());
}); });
setExclusiveZone(interface->exclusionZone()); setExclusiveZone(m_interface->exclusionZone());
connect(interface, &Window::exclusionZoneChanged, this, [this, interface]() { connect(m_interface, &Window::exclusionZoneChanged, this, [this]() {
setExclusiveZone(interface->exclusionZone()); setExclusiveZone(m_interface->exclusionZone());
});
setExclusiveEdge(m_interface->exclusiveEdge());
connect(m_interface, &Window::exclusiveEdgeChanged, this, [this]() {
setExclusiveEdge(m_interface->exclusiveEdge());
}); });
setMargins(interface->margins()); setMargins(m_interface->margins());
connect(interface, &Window::marginsChanged, this, [this, interface]() { connect(m_interface, &Window::marginsChanged, this, [this]() {
setMargins(interface->margins()); setMargins(m_interface->margins());
}); });
setKeyboardInteractivity(interface->keyboardInteractivity()); setKeyboardInteractivity(m_interface->keyboardInteractivity());
connect(interface, &Window::keyboardInteractivityChanged, this, [this, interface]() { connect(m_interface, &Window::keyboardInteractivityChanged, this, [this]() {
setKeyboardInteractivity(interface->keyboardInteractivity()); setKeyboardInteractivity(m_interface->keyboardInteractivity());
}); });
QSize size = window->surfaceSize(); QSize size = window->surfaceSize();
const Window::Anchors anchors = interface->anchors(); const Window::Anchors anchors = m_interface->anchors();
if ((anchors & Window::AnchorLeft) && (anchors & Window::AnchorRight)) { if ((anchors & Window::AnchorLeft) && (anchors & Window::AnchorRight)) {
size.setWidth(0); size.setWidth(0);
} }
@ -74,12 +79,17 @@ QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShell *shell, QtWaylandC
QWaylandLayerSurface::~QWaylandLayerSurface() QWaylandLayerSurface::~QWaylandLayerSurface()
{ {
if (m_waitForSyncCallback) {
wl_callback_destroy(m_waitForSyncCallback);
}
destroy(); destroy();
} }
void QWaylandLayerSurface::zwlr_layer_surface_v1_closed() void QWaylandLayerSurface::zwlr_layer_surface_v1_closed()
{ {
window()->window()->close(); if (m_interface->closeOnDismissed()) {
window()->window()->close();
}
} }
void QWaylandLayerSurface::zwlr_layer_surface_v1_configure(uint32_t serial, uint32_t width, uint32_t height) void QWaylandLayerSurface::zwlr_layer_surface_v1_configure(uint32_t serial, uint32_t width, uint32_t height)
@ -90,7 +100,7 @@ void QWaylandLayerSurface::zwlr_layer_surface_v1_configure(uint32_t serial, uint
if (!m_configured) { if (!m_configured) {
m_configured = true; m_configured = true;
window()->resizeFromApplyConfigure(m_pendingSize); window()->resizeFromApplyConfigure(m_pendingSize);
window()->handleExpose(QRect(QPoint(), m_pendingSize)); sendExpose();
} else { } else {
// Later configures are resizes, so we have to queue them up for a time when we // Later configures are resizes, so we have to queue them up for a time when we
// are not painting to the window. // are not painting to the window.
@ -98,6 +108,17 @@ void QWaylandLayerSurface::zwlr_layer_surface_v1_configure(uint32_t serial, uint
} }
} }
void QWaylandLayerSurface::attachPopup(QtWaylandClient::QWaylandShellSurface *popup)
{
std::any anyRole = popup->surfaceRole();
if (auto role = std::any_cast<::xdg_popup *>(&anyRole)) {
get_popup(*role);
} else {
qCWarning(LAYERSHELLQT) << "Cannot attach popup of unknown type";
}
}
void QWaylandLayerSurface::applyConfigure() void QWaylandLayerSurface::applyConfigure()
{ {
window()->resizeFromApplyConfigure(m_pendingSize); window()->resizeFromApplyConfigure(m_pendingSize);
@ -106,6 +127,7 @@ void QWaylandLayerSurface::applyConfigure()
void QWaylandLayerSurface::setAnchor(uint anchor) void QWaylandLayerSurface::setAnchor(uint anchor)
{ {
set_anchor(anchor); set_anchor(anchor);
setWindowGeometry(window()->windowContentGeometry());
} }
void QWaylandLayerSurface::setExclusiveZone(int32_t zone) void QWaylandLayerSurface::setExclusiveZone(int32_t zone)
@ -113,6 +135,13 @@ void QWaylandLayerSurface::setExclusiveZone(int32_t zone)
set_exclusive_zone(zone); set_exclusive_zone(zone);
} }
void QWaylandLayerSurface::setExclusiveEdge(uint32_t edge)
{
if (zwlr_layer_surface_v1_get_version(object()) >= ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_EDGE_SINCE_VERSION) {
set_exclusive_edge(edge);
}
}
void QWaylandLayerSurface::setMargins(const QMargins &margins) void QWaylandLayerSurface::setMargins(const QMargins &margins)
{ {
set_margin(margins.top(), margins.right(), margins.bottom(), margins.left()); set_margin(margins.top(), margins.right(), margins.bottom(), margins.left());
@ -129,4 +158,111 @@ void QWaylandLayerSurface::setLayer(uint32_t layer)
set_layer(layer); set_layer(layer);
} }
void QWaylandLayerSurface::setWindowGeometry(const QRect &geometry)
{
// if we are setting it to the last size we were configured at, we don't need to do anything
if (geometry.size() == m_pendingSize && !m_waitForSyncCallback) {
return;
}
const bool horizontallyConstrained = m_interface->anchors().testFlags({Window::AnchorLeft, Window::AnchorRight});
const bool verticallyConstrained = m_interface->anchors().testFlags({Window::AnchorTop, Window::AnchorBottom});
QSize size = geometry.size();
if (horizontallyConstrained) {
size.setWidth(0);
}
if (verticallyConstrained) {
size.setHeight(0);
}
set_size(size.width(), size.height());
requestWaylandSync();
} }
bool QWaylandLayerSurface::requestActivate()
{
QWaylandXdgActivationV1 *activation = m_shell->activation();
if (!activation->isActive()) {
return false;
}
if (!m_activationToken.isEmpty()) {
activation->activate(m_activationToken, window()->wlSurface());
m_activationToken = {};
return true;
} else {
const auto focusWindow = QGuiApplication::focusWindow();
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;
}
}
return false;
}
void QWaylandLayerSurface::setXdgActivationToken(const QString &token)
{
m_activationToken = token;
}
void QWaylandLayerSurface::requestXdgActivationToken(quint32 serial)
{
QWaylandXdgActivationV1 *activation = m_shell->activation();
if (!activation->isActive()) {
return;
}
auto tokenProvider = activation->requestXdgActivationToken(
window()->display(), window()->wlSurface(), serial, QString());
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_waitForSyncCallback = nullptr;
layerSurface->sendExpose();
}
};
void QWaylandLayerSurface::requestWaylandSync()
{
if (m_waitForSyncCallback) {
return;
}
m_waitForSyncCallback = wl_display_sync(m_window->display()->wl_display());
wl_callback_add_listener(m_waitForSyncCallback, &syncCallbackListener, this);
}
void QWaylandLayerSurface::handleWaylandSyncDone()
{
if (!window()->isExposed()) {
return;
}
sendExpose();
}
void QWaylandLayerSurface::sendExpose()
{
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
window()->handleExpose(QRect(QPoint(), m_pendingSize));
#else
window()->sendRecursiveExposeEvent();
#endif
}
}

View File

@ -10,40 +10,61 @@
#include <wayland-client.h> #include <wayland-client.h>
#include "qwaylandlayershellintegration_p.h"
#include "layershellqt_export.h" #include "layershellqt_export.h"
#include <QtWaylandClient/private/qwaylandshellsurface_p.h> #include <QtWaylandClient/private/qwaylandshellsurface_p.h>
#include <qwayland-wlr-layer-shell-unstable-v1.h> #include <qwayland-wlr-layer-shell-unstable-v1.h>
namespace LayerShellQt namespace LayerShellQt
{ {
class QWaylandLayerShell;
class Window;
class LAYERSHELLQT_EXPORT QWaylandLayerSurface : public QtWaylandClient::QWaylandShellSurface, public QtWayland::zwlr_layer_surface_v1 class LAYERSHELLQT_EXPORT QWaylandLayerSurface : public QtWaylandClient::QWaylandShellSurface, public QtWayland::zwlr_layer_surface_v1
{ {
Q_OBJECT Q_OBJECT
public: public:
QWaylandLayerSurface(QWaylandLayerShell *shell, QtWaylandClient::QWaylandWindow *window); QWaylandLayerSurface(QWaylandLayerShellIntegration *shell, QtWaylandClient::QWaylandWindow *window);
~QWaylandLayerSurface() override; ~QWaylandLayerSurface() override;
bool isExposed() const override bool isExposed() const override
{ {
return m_configured; return m_configured && !m_waitForSyncCallback;
} }
void attachPopup(QtWaylandClient::QWaylandShellSurface *popup) override;
void setAnchor(uint32_t anchor); void setAnchor(uint32_t anchor);
void setExclusiveZone(int32_t zone); void setExclusiveZone(int32_t zone);
void setExclusiveEdge(uint32_t edge);
void setMargins(const QMargins &margins); void setMargins(const QMargins &margins);
void setKeyboardInteractivity(uint32_t interactivity); void setKeyboardInteractivity(uint32_t interactivity);
void setLayer(uint32_t layer); void setLayer(uint32_t layer);
void applyConfigure() override; void applyConfigure() override;
void setWindowGeometry(const QRect &geometry) override;
bool requestActivate() override;
void setXdgActivationToken(const QString &token) override;
void requestXdgActivationToken(quint32 serial) override;
private: 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_configure(uint32_t serial, uint32_t width, uint32_t height) override;
void zwlr_layer_surface_v1_closed() override; void zwlr_layer_surface_v1_closed() override;
QWaylandLayerShellIntegration *m_shell;
LayerShellQt::Window *m_interface;
QtWaylandClient::QWaylandWindow *m_window;
QSize m_pendingSize; QSize m_pendingSize;
QString m_activationToken;
bool m_configured = false; bool m_configured = false;
static const wl_callback_listener syncCallbackListener;
struct wl_callback *m_waitForSyncCallback = nullptr;
}; };
} }

View File

@ -0,0 +1,44 @@
/** Copyright (C) 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#include "qwaylandxdgactivationv1_p.h"
#include <QtWaylandClient/private/qwaylanddisplay_p.h>
#include <QtWaylandClient/private/qwaylandinputdevice_p.h>
QWaylandXdgActivationV1::QWaylandXdgActivationV1()
: QWaylandClientExtensionTemplate<QWaylandXdgActivationV1>(1)
{
initialize();
}
QWaylandXdgActivationV1::~QWaylandXdgActivationV1()
{
if (isActive()) {
destroy();
}
}
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;
provider->init(wl);
if (surface) {
provider->set_surface(surface);
}
if (!app_id.isEmpty()) {
provider->set_app_id(app_id);
}
if (serial && display->lastInputDevice()) {
provider->set_serial(*serial, display->lastInputDevice()->wl_seat());
}
provider->commit();
return provider;
}
#include "moc_qwaylandxdgactivationv1_p.cpp"

View File

@ -0,0 +1,48 @@
/** Copyright (C) 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
#ifndef QWAYLANDXDGACTIVATIONV1_P_H
#define QWAYLANDXDGACTIVATIONV1_P_H
#include "qwayland-xdg-activation-v1.h"
#include <QObject>
#include <QtWaylandClient/QWaylandClientExtension>
namespace QtWaylandClient
{
class QWaylandDisplay;
class QWaylandSurface;
}
class QWaylandXdgActivationTokenV1 : public QObject, public QtWayland::xdg_activation_token_v1
{
Q_OBJECT
public:
~QWaylandXdgActivationTokenV1() override
{
destroy();
}
protected:
void xdg_activation_token_v1_done(const QString &token) override
{
Q_EMIT done(token);
}
Q_SIGNALS:
void done(const QString &token);
};
class QWaylandXdgActivationV1 : public QWaylandClientExtensionTemplate<QWaylandXdgActivationV1>, public QtWayland::xdg_activation_v1
{
public:
QWaylandXdgActivationV1();
~QWaylandXdgActivationV1() override;
QWaylandXdgActivationTokenV1 *
requestXdgActivationToken(QtWaylandClient::QWaylandDisplay *display, struct ::wl_surface *surface, std::optional<uint32_t> serial, const QString &app_id);
};
#endif // QWAYLANDXDGACTIVATIONV1_P_H

View File

@ -25,7 +25,7 @@
THIS SOFTWARE. THIS SOFTWARE.
</copyright> </copyright>
<interface name="zwlr_layer_shell_v1" version="4"> <interface name="zwlr_layer_shell_v1" version="5">
<description summary="create surfaces that are layers of the desktop"> <description summary="create surfaces that are layers of the desktop">
Clients can use this interface to assign the surface_layer role to Clients can use this interface to assign the surface_layer role to
wl_surfaces. Such surfaces are assigned to a "layer" of the output and wl_surfaces. Such surfaces are assigned to a "layer" of the output and
@ -100,7 +100,7 @@
</request> </request>
</interface> </interface>
<interface name="zwlr_layer_surface_v1" version="4"> <interface name="zwlr_layer_surface_v1" version="5">
<description summary="layer metadata interface"> <description summary="layer metadata interface">
An interface that may be implemented by a wl_surface, for surfaces that An interface that may be implemented by a wl_surface, for surfaces that
are designed to be rendered as a layer of a stacked desktop-like are designed to be rendered as a layer of a stacked desktop-like
@ -367,6 +367,7 @@
<entry name="invalid_size" value="1" summary="size is invalid"/> <entry name="invalid_size" value="1" summary="size is invalid"/>
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/> <entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
<entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/> <entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
<entry name="invalid_exclusive_edge" value="4" summary="exclusive edge is invalid given the surface anchors"/>
</enum> </enum>
<enum name="anchor" bitfield="true"> <enum name="anchor" bitfield="true">
@ -386,5 +387,21 @@
</description> </description>
<arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/> <arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
</request> </request>
<!-- Version 5 additions -->
<request name="set_exclusive_edge" since="5">
<description summary="set the edge the exclusive zone will be applied to">
Requests an edge for the exclusive zone to apply. The exclusive
edge will be automatically deduced from anchor points when possible,
but when the surface is anchored to a corner, it will be necessary
to set it explicitly to disambiguate, as it is not possible to deduce
which one of the two corner edges should be used.
The edge must be one the surface is anchored to, otherwise the
invalid_exclusive_edge protocol error will be raised.
</description>
<arg name="edge" type="uint"/>
</request>
</interface> </interface>
</protocol> </protocol>

View File

@ -14,7 +14,6 @@
#include <QMetaEnum> #include <QMetaEnum>
#include <interfaces/shell.h>
#include <interfaces/window.h> #include <interfaces/window.h>
using namespace LayerShellQt; using namespace LayerShellQt;
@ -33,7 +32,7 @@ template<typename T>
T stringToEnum(QMetaEnum metaEnum, const QString &str) T stringToEnum(QMetaEnum metaEnum, const QString &str)
{ {
T ret = {}; T ret = {};
const auto splitted = str.split(QLatin1Char('|')); const auto splitted = str.split(QLatin1Char('|'), Qt::SkipEmptyParts);
for (const auto &value : splitted) { for (const auto &value : splitted) {
ret |= T(metaEnum.keyToValue(qPrintable(value))); ret |= T(metaEnum.keyToValue(qPrintable(value)));
} }
@ -42,7 +41,7 @@ T stringToEnum(QMetaEnum metaEnum, const QString &str)
class BasicWindow : public QRasterWindow class BasicWindow : public QRasterWindow
{ {
void paintEvent(QPaintEvent *) void paintEvent(QPaintEvent *) override
{ {
QPainter p(this); QPainter p(this);
p.fillRect(QRect(0, 0, width(), height()), Qt::red); p.fillRect(QRect(0, 0, width(), height()), Qt::red);
@ -51,8 +50,6 @@ class BasicWindow : public QRasterWindow
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
Shell::useLayerShell();
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
const auto layerMetaEnum = QMetaEnum::fromType<Window::Layer>(); const auto layerMetaEnum = QMetaEnum::fromType<Window::Layer>();
@ -79,6 +76,8 @@ int main(int argc, char **argv)
BasicWindow window; BasicWindow window;
LayerShellQt::Window *layerShell = LayerShellQt::Window::get(&window); LayerShellQt::Window *layerShell = LayerShellQt::Window::get(&window);
layerShell->setLayer(Window::LayerBottom);
if (parser.isSet(marginsOption)) { if (parser.isSet(marginsOption)) {
int margins = parser.value(marginsOption).toInt(); int margins = parser.value(marginsOption).toInt();
layerShell->setMargins({margins, margins, margins, margins}); layerShell->setMargins({margins, margins, margins, margins});
@ -102,6 +101,10 @@ int main(int argc, char **argv)
window.show(); window.show();
BasicWindow window2;
window2.resize(400, 400);
window2.show();
// just so you don't block yourself out whilst testing // just so you don't block yourself out whilst testing
QTimer::singleShot(5000, &app, &QGuiApplication::quit); QTimer::singleShot(5000, &app, &QGuiApplication::quit);
return app.exec(); return app.exec();

110
tests/quicktest.qml Normal file
View File

@ -0,0 +1,110 @@
/*
* SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleix.pol_gonzalez@mercedes-benz.com>
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/
import QtQuick 2.15
import org.kde.layershell 1.0 as LayerShell
Item
{
Text {
text: "A normal Window"
anchors.centerIn: parent
}
Window {
LayerShell.Window.anchors: LayerShell.Window.AnchorLeft
LayerShell.Window.layer: LayerShell.Window.LayerBackground
LayerShell.Window.exclusionZone: -1
width: 200
height: 150
Rectangle {
anchors.fill: parent
color: "green"
Text {
anchors.centerIn: parent
text: "left bg"
}
}
visible: true
}
Window {
LayerShell.Window.scope: "dock"
LayerShell.Window.anchors: LayerShell.Window.AnchorLeft
LayerShell.Window.layer: LayerShell.Window.LayerTop
LayerShell.Window.exclusionZone: width
width: 100
height: 100
Rectangle {
anchors.fill: parent
color: "red"
Text {
anchors.centerIn: parent
text: "left"
}
}
visible: true
}
Window {
LayerShell.Window.scope: "normal"
LayerShell.Window.anchors: LayerShell.Window.AnchorRight
width: 100
height: 100
Rectangle {
anchors.fill: parent
color: "red"
Text {
anchors.centerIn: parent
text: "right"
}
}
visible: true
}
Window {
LayerShell.Window.scope: "normal"
LayerShell.Window.anchors: LayerShell.Window.AnchorTop
width: 100
height: 100
Rectangle {
anchors.fill: parent
color: "red"
Text {
anchors.centerIn: parent
text: "top"
}
}
visible: true
}
Window {
LayerShell.Window.scope: "normal"
LayerShell.Window.anchors: LayerShell.Window.AnchorBottom
width: 100
height: 100
Rectangle {
anchors.fill: parent
color: "red"
Text {
anchors.centerIn: parent
text: "bottom"
}
}
visible: true
}
}