Compare commits

...

44 Commits

Author SHA1 Message Date
a921ab332c Update version number for 5.26.3
GIT_SILENT
2022-11-08 11:31:04 +00:00
c18d8d63df Update version number for 5.26.2
GIT_SILENT
2022-10-25 14:04:08 +01:00
e82e03a9cc Update version number for 5.26.1
GIT_SILENT
2022-10-18 11:02:39 +01:00
7df446ed15 Update version number for 5.26.0
GIT_SILENT
2022-10-06 12:28:31 +01:00
9b5a39922b Update version number for 5.25.90
GIT_SILENT
2022-09-15 11:50:08 +01:00
91db77acc2 Update kf5 version requirement to 5.98.0
GIT_SILENT
2022-09-15 11:10:38 +01:00
5af50ac3ee Add FreeBSD Qt6 CI support 2022-08-27 14:40:53 +02:00
7bfac45da8 Make fully reuse compliant 2022-08-23 21:52:18 +02:00
7bd63669ea Use change signals on Window interface class.
QWaylandLayerSurface pulled data from the Window on startup. The Window
pushed data into the QWaylandLayerSurface on changes. Having two
patterns is a sign of something being off.

This moves everything to a single design, pulling from the public
interface. This allows us to drop a code path that meddles with
QWaylandWindow internals.
2022-06-30 08:09:21 +00:00
13961ef7b9 Update qt5 version requirement to 5.15.2
GIT_SILENT
2022-06-27 10:02:56 +01:00
ff6155a5af Update kf5 version requirement to 5.94
GIT_SILENT
2022-06-27 10:02:56 +01:00
34e7c90c2d Mark required deps as required
BUG: 454912
2022-06-06 12:26:10 +02:00
563b61c75c Update version number for 5.25.80
GIT_SILENT
2022-05-19 12:26:13 +01:00
404e9851d1 Update version number for 5.24.90
GIT_SILENT
2022-05-19 10:41:53 +01:00
345676a4a1 Minor api doc improvement for api.kde.org 2022-05-04 10:43:03 +00:00
ae883df5b1 Build with Qt6 2022-03-06 10:54:56 +01:00
9f8b8c9731 Add desiredScreen property to LayerShellQt::Window
If the property is set, the compositor will try to put the window on the
given output. If not set, the compositer will decide where to put the
window (usually the active output). The motivation for this change is the
ability for KRunner to always appear on the active output.
2022-01-26 09:04:14 +01:00
14982d6dc7 Update version number for 5.24.80
GIT_SILENT
2022-01-13 11:56:23 +00:00
e557008b56 Update version number for 5.23.90
GIT_SILENT
2022-01-13 10:34:20 +00:00
80f6a629bb GIT_SILENT: add missing override 2021-10-27 13:43:28 +02:00
662afeba22 Update kf5 version requirement to 5.86
GIT_SILENT
2021-10-11 20:48:40 +01:00
3dac2dd012 Add FreeBSD CI 2021-10-07 01:27:09 +02:00
5b280a4602 Add Linux CI 2021-10-05 15:14:20 +02:00
eb6fb8ab13 Add .kde-ci.yml 2021-10-05 15:14:05 +02:00
06815f2817 Update version number for 5.23.80
GIT_SILENT
2021-09-16 10:34:01 +01:00
dc07e14630 Update version number for 5.22.90
GIT_SILENT
2021-09-16 09:21:32 +01:00
4a439143ba Fix a crash when creating views on the placeholder screen
When a compositor has no outputs listed Qt creates a dummy placeholder
QScreen object that does not represent a wl_output.

This should still be fixed in the clients to not create a view, it's
wasteful and probably will still have plenty of other bugs, hence the
giant warning, but it's still worth guarding.

CCBUG: 439096
2021-09-15 22:55:35 +00:00
331505a4d2 Explicitly set KDE_COMPILERSETTINGS_LEVEL 2021-08-24 22:41:46 +02:00
f6084b2c43 Fix compile with last ecm.
("we have disabled the C and C++ specific compiler extensions.")
2021-06-28 07:51:57 +02:00
9af3c35020 Mark .gitignore file as non-copyrightable 2021-05-21 15:35:56 +02:00
525fa9b819 Update version number for 5.22.80
GIT_SILENT
2021-05-13 15:09:54 +01:00
1021ca5ab5 Update version number for 5.21.90
GIT_SILENT
2021-05-13 10:21:51 +01:00
204eeaf1c9 Update kf5 version requirement to 5.82
GIT_SILENT
2021-05-13 09:56:28 +01:00
42a87ad728 Update .gitignore 2021-05-09 17:54:28 +00:00
7d46bd63c5 Port away from ECMSetupVersion's deprecated *_VERSION_STRING CMake variable
GIT_SILENT
2021-05-04 23:35:44 +02:00
710152a7f3 GIT_SILENT Add auto generated files to .gitignore 2021-05-03 14:54:12 +02:00
8d1aeb7bf1 Store QWaylandLayerShell using a QScopedPointer
This is to ensure that the QWaylandLayerShell will be eventually
destroyed.
2021-04-20 16:02:33 +00:00
93f3ed2c07 Fix a memory leak
The dynamically allocated QtWayland::zwlr_layer_shell_v1 object in
QWaylandLayerShellIntegration::registryLayer() isn't deleted anywhere.
2021-04-20 16:02:33 +00:00
efd32eed53 Add missing parenthesis
This is to make the compiler happier.
2021-04-20 18:38:24 +03:00
11a811061f Introduce KeyboardInteractivity enum 2021-04-20 15:27:43 +03:00
549f5ad682 Link against xkbcommon
Otherwise the build fails with missing xkbcommon headers
2021-04-15 20:46:25 +02:00
cd409ed50e Add width and height to test
This is needed if we are not anchoring to the full screen
2021-04-15 09:59:18 +01:00
2b1219cfdd Ensure we can set per-window properties before the intial commit
In the current implementation we cannot use a LayerShellQt before the
shell surface is created.

At the moment a shell surface is created, the constructor is run and
then QtWayland commits the current state. This means the compositor
configures the window before a client has any chance to set anchors or
margins.

This works whilst we're just being a simple fullscreen window, but won't
scale for plasmashell in the future.

This patch makes LayerShellQt::Window always creatable, and we can set
and cache properties before the platform window is created, just like
one can on QWindow and XDGShell properties.

This also makes it less potentially crashy as ::get always returns a
valid result, and
sets up the public API to be QML-able as an attached property in future.

Co-authored on Aleix's patch for the unit test
2021-04-15 09:58:57 +01:00
29d0078909 Ensure we can set per-window properties before the intial commit
In the current implementation we cannot use a LayerShellQt before the
shell surface is created.

At the moment a shell surface is created, the constructor is run and
then QtWayland commits the current state. This means the compositor
configures the window before a client has any chance to set anchors or
margins.

This works whilst we're just being a simple fullscreen window, but won't
scale for plasmashell in the future.

This patch makes LayerShellQt::Window always creatable, and we can set
and cache properties before the platform window is created, just like
one can on QWindow and XDGShell properties.

This also makes it less potentially crashy as ::get always returns a
valid result, and
sets up the public API to be QML-able as an attached property in future.

Co-authored on Aleix's patch for the unit test
2021-04-14 23:49:30 +01:00
19 changed files with 479 additions and 99 deletions

29
.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
# SPDX-License-Identifier: CC0-1.0
# SPDX-FileCopyrightText: none
# Ignore the following files
.vscode
*~
*.[oa]
*.diff
*.kate-swp
*.kdev4
.kdev_include_paths
*.kdevelop.pcs
*.moc
*.moc.cpp
*.orig
*.user
.*.swp
.swp.*
Doxyfile
Makefile
avail
random_seed
/build*/
CMakeLists.txt.user*
.clang-format
/compile_commands.json
.clangd
.idea
/cmake-build*
.cache

9
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,9 @@
# SPDX-FileCopyrightText: None
# SPDX-License-Identifier: CC0-1.0
include:
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/reuse-lint.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd-qt6.yml

7
.kde-ci.yml Normal file
View File

@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: None
# SPDX-License-Identifier: CC0-1.0
Dependencies:
- 'on': ['@all']
'require':
'frameworks/extra-cmake-modules': '@latest'

View File

@ -4,17 +4,18 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(layershellqt) project(layershellqt)
set(PROJECT_VERSION "5.21.80") set(PROJECT_VERSION "5.26.3")
set(PROJECT_VERSION_MAJOR 5) set(PROJECT_VERSION_MAJOR 5)
set(QT_MIN_VERSION "5.15.0") set(CMAKE_C_STANDARD 99)
set(KF5_MIN_VERSION "5.78")
set(QT_MIN_VERSION "5.15.2")
set(KF5_MIN_VERSION "5.98.0")
set(KDE_COMPILERSETTINGS_LEVEL "5.82")
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS WaylandClient Qml)
find_package(Qt5XkbCommonSupport REQUIRED PRIVATE)
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${ECM_MODULE_PATH}) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${ECM_MODULE_PATH})
@ -29,10 +30,17 @@ include(GenerateExportHeader)
include(KDEClangFormat) include(KDEClangFormat)
include(ECMQtDeclareLoggingCategory) include(ECMQtDeclareLoggingCategory)
find_package(WaylandScanner) find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS WaylandClient Qml)
find_package(QtWaylandScanner) if (QT_MAJOR_VERSION EQUAL "5")
find_package(Qt5XkbCommonSupport REQUIRED PRIVATE)
find_package(QtWaylandScanner REQUIRED)
endif()
find_package(WaylandScanner REQUIRED)
find_package(Wayland 1.3 COMPONENTS Client Server) find_package(Wayland 1.3 COMPONENTS Client Server)
find_package(WaylandProtocols) find_package(WaylandProtocols REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(XKBCOMMON xkbcommon REQUIRED IMPORTED_TARGET)
set_package_properties(Wayland PROPERTIES set_package_properties(Wayland PROPERTIES
TYPE REQUIRED) TYPE REQUIRED)

View File

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

17
metainfo.yaml Normal file
View File

@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: None
# SPDX-License-Identifier: CC0-1.0
maintainer:
- vladz
description: Layer Shell Qt
platforms:
- name: Linux
- name: FreeBSD
portingAid: false
deprecated: false
release: true
group: Plasma
public_lib: true
public_source_dirs:
- src/interfaces

View File

@ -3,8 +3,16 @@
remove_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS) remove_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS)
ecm_add_qtwayland_client_protocol(LAYER_SHELL_SOURCES PROTOCOL ${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml BASENAME xdg-shell) add_library(LayerShellQtInterface)
ecm_add_qtwayland_client_protocol(LAYER_SHELL_SOURCES PROTOCOL wlr-layer-shell-unstable-v1.xml BASENAME wlr-layer-shell-unstable-v1) if (QT_MAJOR_VERSION EQUAL "5")
ecm_add_qtwayland_client_protocol(LAYER_SHELL_SOURCES PROTOCOL ${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml BASENAME xdg-shell)
ecm_add_qtwayland_client_protocol(LAYER_SHELL_SOURCES PROTOCOL wlr-layer-shell-unstable-v1.xml BASENAME wlr-layer-shell-unstable-v1)
else()
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(LAYER_SHELL_SOURCES
HEADER HEADER
@ -15,19 +23,25 @@ ecm_qt_declare_logging_category(LAYER_SHELL_SOURCES
layershellqt layershellqt
) )
add_library(LayerShellQtInterface SHARED qwaylandlayersurface.cpp interfaces/window.cpp interfaces/shell.cpp qwaylandlayershellintegration.cpp qwaylandlayershell.cpp ${LAYER_SHELL_SOURCES}) target_sources(LayerShellQtInterface PRIVATE qwaylandlayersurface.cpp interfaces/window.cpp interfaces/shell.cpp qwaylandlayershellintegration.cpp qwaylandlayershell.cpp ${LAYER_SHELL_SOURCES})
target_link_libraries(LayerShellQtInterface PRIVATE Qt::Gui Qt::WaylandClientPrivate Qt::XkbCommonSupportPrivate Wayland::Client) target_link_libraries(LayerShellQtInterface PRIVATE Qt::Gui Qt::WaylandClientPrivate Wayland::Client PkgConfig::XKBCOMMON)
if (TARGET Qt::XkbCommonSupportPrivate)
target_link_libraries(LayerShellQtInterface PRIVATE Qt::XkbCommonSupportPrivate)
endif()
target_include_directories(LayerShellQtInterface PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/LayerShellQt>" target_include_directories(LayerShellQtInterface PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/LayerShellQt>"
INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR}/>" INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR}/>"
) )
set_target_properties(LayerShellQtInterface PROPERTIES VERSION ${LAYERSHELLQT_VERSION_STRING} set_target_properties(LayerShellQtInterface PROPERTIES VERSION ${LAYERSHELLQT_VERSION}
SOVERSION ${LAYERSHELLQT_SOVERSION} SOVERSION ${LAYERSHELLQT_SOVERSION}
EXPORT_NAME Interface EXPORT_NAME Interface
) )
add_library(layer-shell SHARED qwaylandlayershellintegrationplugin.cpp) add_library(layer-shell SHARED qwaylandlayershellintegrationplugin.cpp)
target_link_libraries(layer-shell LayerShellQtInterface Qt::WaylandClient Qt::WaylandClientPrivate Qt::XkbCommonSupportPrivate Wayland::Client) target_link_libraries(layer-shell LayerShellQtInterface Qt::WaylandClient Qt::WaylandClientPrivate Wayland::Client PkgConfig::XKBCOMMON)
if (TARGET Qt::XkbCommonSupportPrivate)
target_link_libraries(layer-shell Qt::XkbCommonSupportPrivate)
endif()
ecm_generate_headers(LayerShellQt_HEADERS ecm_generate_headers(LayerShellQt_HEADERS
HEADER_NAMES HEADER_NAMES
@ -45,7 +59,7 @@ generate_export_header(LayerShellQtInterface
) )
install(TARGETS layer-shell install(TARGETS layer-shell
LIBRARY DESTINATION ${QT_PLUGIN_INSTALL_DIR}/wayland-shell-integration) LIBRARY DESTINATION ${KDE_INSTALL_QTPLUGINDIR}/wayland-shell-integration)
install(TARGETS LayerShellQtInterface EXPORT LayerShellQtTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS LayerShellQtInterface EXPORT LayerShellQtTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})

View File

@ -6,8 +6,8 @@
#include "shell.h" #include "shell.h"
#include <QByteArray> #include <QByteArray>
#include <qglobal.h>
#include <layershellqt_logging.h> #include <layershellqt_logging.h>
#include <qglobal.h>
using namespace LayerShellQt; using namespace LayerShellQt;

View File

@ -8,6 +8,8 @@
#define LAYERSHELLQTSHELL_H #define LAYERSHELLQTSHELL_H
#include "layershellqt_export.h" #include "layershellqt_export.h"
#include "window.h"
#include <QString>
namespace LayerShellQt namespace LayerShellQt
{ {

View File

@ -5,68 +5,124 @@
*/ */
#include "window.h" #include "window.h"
#include "../qwaylandlayersurface_p.h"
#include <layershellqt_logging.h> #include <layershellqt_logging.h>
#include <private/qwaylandshellsurface_p.h>
#include <private/qwaylandwindow_p.h> #include <QPointer>
using namespace LayerShellQt; using namespace LayerShellQt;
class LayerShellQt::WindowPrivate class LayerShellQt::WindowPrivate
{ {
public: public:
WindowPrivate(QWaylandLayerSurface *surface) WindowPrivate(QWindow *window)
: surface(surface) : parentWindow(window)
{ {
} }
QWaylandLayerSurface *const surface; QWindow *parentWindow;
QString scope = QStringLiteral("window");
Window::Anchors anchors = {Window::AnchorTop | Window::AnchorBottom | Window::AnchorLeft | Window::AnchorRight};
int32_t exclusionZone = 0;
Window::KeyboardInteractivity keyboardInteractivity = Window::KeyboardInteractivityExclusive;
Window::Layer layer = Window::LayerTop;
QMargins margins;
QPointer<QScreen> desiredOutput;
}; };
Window::~Window() = default; static QMap<QWindow *, Window *> s_map;
void Window::setAnchor(Anchor anchor) Window::~Window()
{ {
d->surface->setAnchor(anchor); s_map.remove(d->parentWindow);
}
void Window::setAnchors(Anchors anchors)
{
d->anchors = anchors;
Q_EMIT anchorsChanged();
}
Window::Anchors Window::anchors() const
{
return d->anchors;
} }
void Window::setExclusiveZone(int32_t zone) void Window::setExclusiveZone(int32_t zone)
{ {
d->surface->setExclusiveZone(zone); d->exclusionZone = zone;
Q_EMIT exclusionZoneChanged();
}
int32_t Window::exclusionZone() const
{
return d->exclusionZone;
} }
void Window::setMargins(const QMargins &margins) void Window::setMargins(const QMargins &margins)
{ {
d->surface->setMargins(margins); d->margins = margins;
Q_EMIT marginsChanged();
} }
void Window::setKeyboardInteractivity(bool enabled) QMargins Window::margins() const
{ {
d->surface->setKeyboardInteractivity(enabled); return d->margins;
}
void Window::setKeyboardInteractivity(KeyboardInteractivity interactivity)
{
d->keyboardInteractivity = interactivity;
Q_EMIT keyboardInteractivityChanged();
}
Window::KeyboardInteractivity Window::keyboardInteractivity() const
{
return d->keyboardInteractivity;
} }
void Window::setLayer(Layer layer) void Window::setLayer(Layer layer)
{ {
d->surface->setLayer(layer); d->layer = layer;
} }
Window::Window(WindowPrivate *d) void Window::setScope(const QString &scope)
: d(d)
{ {
d->scope = scope;
// this is static and must be set before the platform window is created
}
QString Window::scope() const
{
return d->scope;
}
Window::Layer Window::layer() const
{
return d->layer;
}
QScreen *Window::desiredOutput() const
{
return d->desiredOutput;
}
void Window::setDesiredOutput(QScreen *output)
{
d->desiredOutput = output;
}
Window::Window(QWindow *window)
: QObject(window)
, d(new WindowPrivate(window))
{
s_map.insert(d->parentWindow, this);
} }
Window *Window::get(QWindow *window) Window *Window::get(QWindow *window)
{ {
auto ww = dynamic_cast<QtWaylandClient::QWaylandWindow *>(window->handle()); auto layerShellWindow = s_map.value(window);
if (!ww) { if (layerShellWindow) {
qCDebug(LAYERSHELLQT) << "window not a wayland window" << window; return layerShellWindow;
return nullptr;
} }
QWaylandLayerSurface *s = qobject_cast<QWaylandLayerSurface *>(ww->shellSurface()); return new Window(window);
if (!s) {
qCDebug(LAYERSHELLQT) << "window not using wlr-layer-shell" << window << ww->shellSurface();
return nullptr;
}
return new Window(new WindowPrivate(s));
} }

View File

@ -9,6 +9,7 @@
#define LAYERSHELLQTWINDOW_H #define LAYERSHELLQTWINDOW_H
#include <QObject> #include <QObject>
#include <QScreen>
#include <QWindow> #include <QWindow>
#include "layershellqt_export.h" #include "layershellqt_export.h"
@ -24,12 +25,13 @@ public:
~Window() override; ~Window() override;
enum Anchor { enum Anchor {
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
AnchorRight = 8, // the right edge of the anchor rectangle AnchorRight = 8, ///< The right edge of the anchor rectangle
}; };
Q_ENUM(Anchor); Q_ENUM(Anchor);
Q_DECLARE_FLAGS(Anchors, Anchor)
/** /**
* This enum type is used to specify the layer where a surface can be put in. * This enum type is used to specify the layer where a surface can be put in.
@ -42,16 +44,64 @@ public:
}; };
Q_ENUM(Layer) Q_ENUM(Layer)
void setAnchor(Anchor anchor); /**
void setExclusiveZone(int32_t zone); * This enum type is used to specify how the layer surface handles keyboard focus.
void setMargins(const QMargins &margins); */
void setKeyboardInteractivity(bool enabled); enum KeyboardInteractivity {
void setLayer(Layer layer); KeyboardInteractivityNone = 0,
KeyboardInteractivityExclusive = 1,
KeyboardInteractivityOnDemand = 2,
};
Q_ENUM(KeyboardInteractivity)
void setAnchors(Anchors anchor);
Anchors anchors() const;
void setExclusiveZone(int32_t zone);
int32_t exclusionZone() const;
void setMargins(const QMargins &margins);
QMargins margins() const;
void setKeyboardInteractivity(KeyboardInteractivity interactivity);
KeyboardInteractivity keyboardInteractivity() const;
void setLayer(Layer layer);
Layer layer() const;
/**
* If set, the compositor will try to put the window on the given screen.
* 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.
* This may be used by a compositor to determine stacking
* order within a given layer.
*
* May also be referred to as a role
*/
void setScope(const QString &scope);
QString scope() const;
/**
* Gets the LayerShell Window for a given Qt Window
* Ownership is not transferred
*/
static Window *get(QWindow *window); static Window *get(QWindow *window);
Q_SIGNALS:
void anchorsChanged();
void exclusionZoneChanged();
void marginsChanged();
void keyboardInteractivityChanged();
void layerChanged();
private: private:
Window(WindowPrivate *d); Window(QWindow *window);
QScopedPointer<WindowPrivate> d; QScopedPointer<WindowPrivate> d;
}; };

View File

@ -10,8 +10,8 @@
namespace LayerShellQt namespace LayerShellQt
{ {
QWaylandLayerShell::QWaylandLayerShell(QtWayland::zwlr_layer_shell_v1 *shell) QWaylandLayerShell::QWaylandLayerShell(::wl_registry *registry, uint32_t id, uint32_t version)
: QtWayland::zwlr_layer_shell_v1(shell->object()) : QtWayland::zwlr_layer_shell_v1(registry, id, version)
{ {
} }

View File

@ -20,8 +20,8 @@ namespace LayerShellQt
class LAYERSHELLQT_EXPORT QWaylandLayerShell : public QtWayland::zwlr_layer_shell_v1 class LAYERSHELLQT_EXPORT QWaylandLayerShell : public QtWayland::zwlr_layer_shell_v1
{ {
public: public:
QWaylandLayerShell(QtWayland::zwlr_layer_shell_v1 *shell); QWaylandLayerShell(::wl_registry *registry, uint32_t id, uint32_t version);
virtual ~QWaylandLayerShell(); ~QWaylandLayerShell() override;
QWaylandLayerSurface *createLayerSurface(QtWaylandClient::QWaylandWindow *window); QWaylandLayerSurface *createLayerSurface(QtWaylandClient::QWaylandWindow *window);
// TODO: Popups // TODO: Popups

View File

@ -18,9 +18,15 @@ QWaylandLayerShellIntegration::QWaylandLayerShellIntegration()
{ {
} }
QWaylandLayerShellIntegration::~QWaylandLayerShellIntegration()
{
}
bool QWaylandLayerShellIntegration::initialize(QtWaylandClient::QWaylandDisplay *display) bool QWaylandLayerShellIntegration::initialize(QtWaylandClient::QWaylandDisplay *display)
{ {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QWaylandShellIntegration::initialize(display); QWaylandShellIntegration::initialize(display);
#endif
display->addRegistryListener(registryLayer, this); display->addRegistryListener(registryLayer, this);
return m_layerShell != nullptr; return m_layerShell != nullptr;
} }
@ -35,7 +41,7 @@ void QWaylandLayerShellIntegration::registryLayer(void *data, struct wl_registry
QWaylandLayerShellIntegration *shell = static_cast<QWaylandLayerShellIntegration *>(data); QWaylandLayerShellIntegration *shell = static_cast<QWaylandLayerShellIntegration *>(data);
if (interface == zwlr_layer_shell_v1_interface.name) if (interface == zwlr_layer_shell_v1_interface.name)
shell->m_layerShell = new QWaylandLayerShell(new QtWayland::zwlr_layer_shell_v1(registry, id, std::min(version, 3u))); shell->m_layerShell.reset(new QWaylandLayerShell(registry, id, std::min(version, 4u)));
} }
} }

View File

@ -21,6 +21,7 @@ class LAYERSHELLQT_EXPORT QWaylandLayerShellIntegration : public QtWaylandClient
{ {
public: public:
QWaylandLayerShellIntegration(); QWaylandLayerShellIntegration();
~QWaylandLayerShellIntegration() override;
bool initialize(QtWaylandClient::QWaylandDisplay *display) override; bool initialize(QtWaylandClient::QWaylandDisplay *display) override;
QtWaylandClient::QWaylandShellSurface *createShellSurface(QtWaylandClient::QWaylandWindow *window) override; QtWaylandClient::QWaylandShellSurface *createShellSurface(QtWaylandClient::QWaylandWindow *window) override;
@ -28,7 +29,7 @@ public:
private: private:
static void registryLayer(void *data, struct wl_registry *registry, uint32_t id, const QString &interface, uint32_t version); static void registryLayer(void *data, struct wl_registry *registry, uint32_t id, const QString &interface, uint32_t version);
QWaylandLayerShell *m_layerShell = nullptr; QScopedPointer<QWaylandLayerShell> m_layerShell;
}; };
} }

View File

@ -5,6 +5,8 @@
* SPDX-License-Identifier: LGPL-3.0-or-later * SPDX-License-Identifier: LGPL-3.0-or-later
*/ */
#include "interfaces/shell.h"
#include "layershellqt_logging.h"
#include "qwaylandlayershell_p.h" #include "qwaylandlayershell_p.h"
#include "qwaylandlayersurface_p.h" #include "qwaylandlayersurface_p.h"
@ -16,14 +18,58 @@ namespace LayerShellQt
{ {
QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShell *shell, QtWaylandClient::QWaylandWindow *window) QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShell *shell, QtWaylandClient::QWaylandWindow *window)
: QtWaylandClient::QWaylandShellSurface(window) : QtWaylandClient::QWaylandShellSurface(window)
, QtWayland::zwlr_layer_surface_v1( , QtWayland::zwlr_layer_surface_v1()
// TODO: Specify namespace
shell->get_layer_surface(window->waylandSurface()->object(),
window->waylandScreen()->output(),
QtWayland::zwlr_layer_shell_v1::layer_top,
QStringLiteral("qt")))
{ {
set_anchor(anchor_top | anchor_bottom | anchor_left | anchor_right); LayerShellQt::Window *interface = Window::get(window->window());
Q_ASSERT(interface);
wl_output *output = nullptr;
QScreen *screen = interface->desiredOutput();
if (screen) {
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
// this will not cast to a QWaylandScreen
if (!waylandScreen) {
qCWarning(LAYERSHELLQT) << "Creating a layer shell for placeholder screen. This will be positioned incorrectly";
} else {
output = waylandScreen->output();
}
}
init(shell->get_layer_surface(window->waylandSurface()->object(), output, interface->layer(), interface->scope()));
connect(interface, &Window::layerChanged, this, [this, interface]() {
setLayer(interface->layer());
});
set_anchor(interface->anchors());
connect(interface, &Window::anchorsChanged, this, [this, interface]() {
set_anchor(interface->anchors());
});
setExclusiveZone(interface->exclusionZone());
connect(interface, &Window::exclusionZoneChanged, this, [this, interface]() {
setExclusiveZone(interface->exclusionZone());
});
setMargins(interface->margins());
connect(interface, &Window::marginsChanged, this, [this, interface]() {
setMargins(interface->margins());
});
setKeyboardInteractivity(interface->keyboardInteractivity());
connect(interface, &Window::keyboardInteractivityChanged, this, [this, interface]() {
setKeyboardInteractivity(interface->keyboardInteractivity());
});
QSize size = window->surfaceSize();
const Window::Anchors anchors = interface->anchors();
if ((anchors & Window::AnchorLeft) && (anchors & Window::AnchorRight)) {
size.setWidth(0);
}
if ((anchors & Window::AnchorTop) && (anchors & Window::AnchorBottom)) {
size.setHeight(0);
}
if (size.isValid() && size != QSize(0, 0)) {
set_size(size.width(), size.height());
}
} }
QWaylandLayerSurface::~QWaylandLayerSurface() QWaylandLayerSurface::~QWaylandLayerSurface()
@ -72,9 +118,9 @@ 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());
} }
void QWaylandLayerSurface::setKeyboardInteractivity(bool enabled) void QWaylandLayerSurface::setKeyboardInteractivity(uint32_t interactivity)
{ {
set_keyboard_interactivity(enabled); set_keyboard_interactivity(interactivity);
} }
void QWaylandLayerSurface::setLayer(uint32_t layer) void QWaylandLayerSurface::setLayer(uint32_t layer)

View File

@ -23,7 +23,7 @@ class LAYERSHELLQT_EXPORT QWaylandLayerSurface : public QtWaylandClient::QWaylan
Q_OBJECT Q_OBJECT
public: public:
QWaylandLayerSurface(QWaylandLayerShell *shell, QtWaylandClient::QWaylandWindow *window); QWaylandLayerSurface(QWaylandLayerShell *shell, QtWaylandClient::QWaylandWindow *window);
virtual ~QWaylandLayerSurface(); ~QWaylandLayerSurface() override;
bool isExposed() const override bool isExposed() const override
{ {
@ -33,7 +33,7 @@ public:
void setAnchor(uint32_t anchor); void setAnchor(uint32_t anchor);
void setExclusiveZone(int32_t zone); void setExclusiveZone(int32_t zone);
void setMargins(const QMargins &margins); void setMargins(const QMargins &margins);
void setKeyboardInteractivity(bool enabled); void setKeyboardInteractivity(uint32_t interactivity);
void setLayer(uint32_t layer); void setLayer(uint32_t layer);
void applyConfigure() override; void applyConfigure() override;

View File

@ -25,7 +25,7 @@
THIS SOFTWARE. THIS SOFTWARE.
</copyright> </copyright>
<interface name="zwlr_layer_shell_v1" version="3"> <interface name="zwlr_layer_shell_v1" version="4">
<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="3"> <interface name="zwlr_layer_surface_v1" version="4">
<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
@ -203,21 +203,85 @@
<arg name="left" type="int"/> <arg name="left" type="int"/>
</request> </request>
<enum name="keyboard_interactivity">
<description summary="types of keyboard interaction possible for a layer shell surface">
Types of keyboard interaction possible for layer shell surfaces. The
rationale for this is twofold: (1) some applications are not interested
in keyboard events and not allowing them to be focused can improve the
desktop experience; (2) some applications will want to take exclusive
keyboard focus.
</description>
<entry name="none" value="0">
<description summary="no keyboard focus is possible">
This value indicates that this surface is not interested in keyboard
events and the compositor should never assign it the keyboard focus.
This is the default value, set for newly created layer shell surfaces.
This is useful for e.g. desktop widgets that display information or
only have interaction with non-keyboard input devices.
</description>
</entry>
<entry name="exclusive" value="1">
<description summary="request exclusive keyboard focus">
Request exclusive keyboard focus if this surface is above the shell surface layer.
For the top and overlay layers, the seat will always give
exclusive keyboard focus to the top-most layer which has keyboard
interactivity set to exclusive. If this layer contains multiple
surfaces with keyboard interactivity set to exclusive, the compositor
determines the one receiving keyboard events in an implementation-
defined manner. In this case, no guarantee is made when this surface
will receive keyboard focus (if ever).
For the bottom and background layers, the compositor is allowed to use
normal focus semantics.
This setting is mainly intended for applications that need to ensure
they receive all keyboard events, such as a lock screen or a password
prompt.
</description>
</entry>
<entry name="on_demand" value="2" since="4">
<description summary="request regular keyboard focus semantics">
This requests the compositor to allow this surface to be focused and
unfocused by the user in an implementation-defined manner. The user
should be able to unfocus this surface even regardless of the layer
it is on.
Typically, the compositor will want to use its normal mechanism to
manage keyboard focus between layer shell surfaces with this setting
and regular toplevels on the desktop layer (e.g. click to focus).
Nevertheless, it is possible for a compositor to require a special
interaction to focus or unfocus layer shell surfaces (e.g. requiring
a click even if focus follows the mouse normally, or providing a
keybinding to switch focus between layers).
This setting is mainly intended for desktop shell components (e.g.
panels) that allow keyboard interaction. Using this option can allow
implementing a desktop shell that can be fully usable without the
mouse.
</description>
</entry>
</enum>
<request name="set_keyboard_interactivity"> <request name="set_keyboard_interactivity">
<description summary="requests keyboard events"> <description summary="requests keyboard events">
Set to 1 to request that the seat send keyboard events to this layer Set how keyboard events are delivered to this surface. By default,
surface. For layers below the shell surface layer, the seat will use layer shell surfaces do not receive keyboard events; this request can
normal focus semantics. For layers above the shell surface layers, the be used to change this.
seat will always give exclusive keyboard focus to the top-most layer
which has keyboard interactivity set to true. This setting is inherited by child surfaces set by the get_popup
request.
Layer surfaces receive pointer, touch, and tablet events normally. If Layer surfaces receive pointer, touch, and tablet events normally. If
you do not want to receive them, set the input region on your surface you do not want to receive them, set the input region on your surface
to an empty region. to an empty region.
Events is double-buffered, see wl_surface.commit. Keyboard interactivity is double-buffered, see wl_surface.commit.
</description> </description>
<arg name="keyboard_interactivity" type="uint"/> <arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
</request> </request>
<request name="get_popup"> <request name="get_popup">
@ -302,6 +366,7 @@
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/> <entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
<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"/>
</enum> </enum>
<enum name="anchor" bitfield="true"> <enum name="anchor" bitfield="true">

View File

@ -4,35 +4,105 @@
* SPDX-License-Identifier: LGPL-3.0-or-later * SPDX-License-Identifier: LGPL-3.0-or-later
*/ */
#include <QCommandLineParser>
#include <QGuiApplication> #include <QGuiApplication>
#include <QQmlApplicationEngine> #include <QPainter>
#include <QRasterWindow>
#include <QTimer>
#include <QWindow>
#include <QMetaEnum>
#include <interfaces/shell.h> #include <interfaces/shell.h>
#include <interfaces/window.h> #include <interfaces/window.h>
using namespace LayerShellQt;
QStringList enumsToStringList(QMetaEnum metaEnum)
{
QStringList ret;
ret.reserve(metaEnum.keyCount());
for (int i = 0; i < metaEnum.keyCount(); ++i) {
ret.append(metaEnum.key(i));
}
return ret;
}
template<typename T>
T stringToEnum(QMetaEnum metaEnum, const QString &str)
{
T ret = {};
const auto splitted = str.split(QLatin1Char('|'));
for (const auto &value : splitted) {
ret |= T(metaEnum.keyToValue(qPrintable(value)));
}
return ret;
}
class BasicWindow : public QRasterWindow
{
void paintEvent(QPaintEvent *)
{
QPainter p(this);
p.fillRect(QRect(0, 0, width(), height()), Qt::red);
}
};
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
LayerShellQt::Shell::useLayerShell(); Shell::useLayerShell();
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.loadData(
"import QtQuick.Controls 2.10\n"
"import QtQuick 2.10\n"
"\n"
"ApplicationWindow {"
" width: 100; height: 100\n"
" visible: true\n"
" Rectangle { color: 'red'; anchors.fill: parent }"
"}"
, const auto layerMetaEnum = QMetaEnum::fromType<Window::Layer>();
QStringLiteral("bananaland:/potato.qml")); const auto anchorMetaEnum = QMetaEnum::fromType<Window::Anchor>();
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [](QObject *object) { QCommandLineParser parser;
auto layerWindow = LayerShellQt::Window::get(qobject_cast<QWindow *>(object)); QCommandLineOption marginsOption(QStringLiteral("margins"), QStringLiteral("Window margins"), QStringLiteral("pixels"), QStringLiteral("0"));
Q_ASSERT(layerWindow); QCommandLineOption scopeOption(QStringLiteral("scope"), QStringLiteral("Window scope"), QStringLiteral("namespace"), QStringLiteral("normal"));
layerWindow->setMargins({50, 50, 50, 50}); QCommandLineOption anchorsOption(QStringLiteral("anchors"),
}); QStringLiteral("Either ") + enumsToStringList(anchorMetaEnum).join(QLatin1String("|")),
QStringLiteral("anchors"),
QStringLiteral("AnchorTop|AnchorBottom|AnchorLeft|AnchorRight"));
QCommandLineOption layerOption(QStringLiteral("layer"),
QStringLiteral("One of ") + enumsToStringList(layerMetaEnum).join(QLatin1String("|")),
QStringLiteral("layer"),
QStringLiteral("LayerTop"));
QCommandLineOption widthOption(QStringLiteral("width"), QStringLiteral("Width of the window"), QStringLiteral("pixels"), QStringLiteral("0"));
QCommandLineOption heightOption(QStringLiteral("height"), QStringLiteral("Height of the window"), QStringLiteral("pixels"), QStringLiteral("0"));
parser.addOptions({marginsOption, scopeOption, anchorsOption, layerOption, widthOption, heightOption});
parser.addHelpOption();
parser.process(app);
BasicWindow window;
LayerShellQt::Window *layerShell = LayerShellQt::Window::get(&window);
if (parser.isSet(marginsOption)) {
int margins = parser.value(marginsOption).toInt();
layerShell->setMargins({margins, margins, margins, margins});
}
if (parser.isSet(scopeOption)) {
layerShell->setScope(parser.value(scopeOption));
}
if (parser.isSet(layerOption)) {
layerShell->setLayer(Window::Layer(layerMetaEnum.keyToValue(qPrintable(parser.value(layerOption)))));
}
if (parser.isSet(anchorsOption)) {
layerShell->setAnchors(stringToEnum<Window::Anchors>(anchorMetaEnum, parser.value(anchorsOption)));
}
if (parser.isSet(widthOption)) {
window.setWidth(parser.value(widthOption).toInt());
}
if (parser.isSet(heightOption)) {
window.setHeight(parser.value(heightOption).toInt());
}
window.show();
// just so you don't block yourself out whilst testing
QTimer::singleShot(5000, &app, &QGuiApplication::quit);
return app.exec(); return app.exec();
} }