Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
if(KWINDOWSYSTEM_X11)
|
||||
add_subdirectory(xcb)
|
||||
endif()
|
||||
if(KWINDOWSYSTEM_WAYLAND)
|
||||
add_subdirectory(wayland)
|
||||
endif()
|
||||
@@ -0,0 +1,70 @@
|
||||
add_library(KF6WindowSystemKWaylandPlugin MODULE)
|
||||
set(wayland_plugin_SRCS
|
||||
plugin.cpp
|
||||
shm.cpp
|
||||
windoweffects.cpp
|
||||
windowshadow.cpp
|
||||
windowsystem.cpp
|
||||
waylandxdgactivationv1.cpp
|
||||
waylandxdgforeignv2.cpp
|
||||
plugin.h
|
||||
windoweffects.h
|
||||
windowshadow.h
|
||||
windowsystem.h
|
||||
waylandxdgactivationv1_p.h
|
||||
)
|
||||
|
||||
|
||||
if (Qt6_VERSION VERSION_GREATER_EQUAL "6.8.0")
|
||||
set(private_code_option "PRIVATE_CODE")
|
||||
endif()
|
||||
qt6_generate_wayland_protocol_client_sources(KF6WindowSystemKWaylandPlugin
|
||||
${private_code_option}
|
||||
FILES
|
||||
${WaylandProtocols_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml
|
||||
${WaylandProtocols_DATADIR}/unstable/xdg-foreign/xdg-foreign-unstable-v2.xml
|
||||
${PLASMA_WAYLAND_PROTOCOLS_DIR}/blur.xml
|
||||
${PLASMA_WAYLAND_PROTOCOLS_DIR}/contrast.xml
|
||||
${PLASMA_WAYLAND_PROTOCOLS_DIR}/slide.xml
|
||||
${PLASMA_WAYLAND_PROTOCOLS_DIR}/plasma-window-management.xml
|
||||
${PLASMA_WAYLAND_PROTOCOLS_DIR}/shadow.xml
|
||||
${Wayland_DATADIR}/wayland.xml
|
||||
)
|
||||
|
||||
|
||||
ecm_qt_declare_logging_category(wayland_plugin_SRCS
|
||||
HEADER logging.h
|
||||
IDENTIFIER KWAYLAND_KWS
|
||||
CATEGORY_NAME kf.windowsystem.wayland
|
||||
OLD_CATEGORY_NAMES org.kde.kf5.kwindowsystem.kwayland
|
||||
DEFAULT_SEVERITY Warning
|
||||
DESCRIPTION "KWindowSystem Wayland Plugin"
|
||||
EXPORT KWINDOWSYSTEM
|
||||
)
|
||||
|
||||
target_sources(KF6WindowSystemKWaylandPlugin PRIVATE ${wayland_plugin_SRCS})
|
||||
|
||||
if(HAVE_MEMFD)
|
||||
target_compile_definitions(KF6WindowSystemKWaylandPlugin PRIVATE -DHAVE_MEMFD)
|
||||
endif()
|
||||
|
||||
|
||||
target_compile_options(KF6WindowSystemKWaylandPlugin PRIVATE -UQT_NO_KEYWORDS)
|
||||
|
||||
pkg_check_modules(XKBCommon REQUIRED IMPORTED_TARGET xkbcommon)
|
||||
|
||||
target_link_libraries(KF6WindowSystemKWaylandPlugin
|
||||
KF6WindowSystem
|
||||
Wayland::Client
|
||||
Qt::GuiPrivate
|
||||
Qt::WaylandClient
|
||||
PkgConfig::XKBCommon
|
||||
)
|
||||
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
KF6WindowSystemKWaylandPlugin
|
||||
DESTINATION
|
||||
${KDE_INSTALL_PLUGINDIR}/kf6/kwindowsystem/
|
||||
)
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "plugin.h"
|
||||
#include "windoweffects.h"
|
||||
#include "windowshadow.h"
|
||||
#include "windowsystem.h"
|
||||
|
||||
KWaylandPlugin::KWaylandPlugin(QObject *parent)
|
||||
: KWindowSystemPluginInterface(parent)
|
||||
{
|
||||
}
|
||||
|
||||
KWaylandPlugin::~KWaylandPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
KWindowEffectsPrivate *KWaylandPlugin::createEffects()
|
||||
{
|
||||
return new WindowEffects();
|
||||
}
|
||||
|
||||
KWindowSystemPrivate *KWaylandPlugin::createWindowSystem()
|
||||
{
|
||||
return new WindowSystem();
|
||||
}
|
||||
|
||||
KWindowShadowTilePrivate *KWaylandPlugin::createWindowShadowTile()
|
||||
{
|
||||
return new WindowShadowTile();
|
||||
}
|
||||
|
||||
KWindowShadowPrivate *KWaylandPlugin::createWindowShadow()
|
||||
{
|
||||
return new WindowShadow();
|
||||
}
|
||||
|
||||
#include "moc_plugin.cpp"
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#ifndef KWINDOWSYSTEM_KWAYLAND_PLUGIN_H
|
||||
#define KWINDOWSYSTEM_KWAYLAND_PLUGIN_H
|
||||
|
||||
#include "kwindowsystemplugininterface_p.h"
|
||||
|
||||
class KWaylandPlugin : public KWindowSystemPluginInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.kde.kwindowsystem.KWindowSystemPluginInterface" FILE "wayland.json")
|
||||
Q_INTERFACES(KWindowSystemPluginInterface)
|
||||
|
||||
public:
|
||||
explicit KWaylandPlugin(QObject *parent = nullptr);
|
||||
~KWaylandPlugin() override;
|
||||
|
||||
KWindowEffectsPrivate *createEffects() override;
|
||||
KWindowSystemPrivate *createWindowSystem() override;
|
||||
KWindowShadowTilePrivate *createWindowShadowTile() override;
|
||||
KWindowShadowPrivate *createWindowShadow() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2023 David Redondo <kde@david-redondo.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "shm.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QImage>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
static constexpr auto version = 1;
|
||||
|
||||
ShmBuffer::ShmBuffer(::wl_buffer *buffer)
|
||||
: QtWayland::wl_buffer(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
ShmBuffer::~ShmBuffer()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
Shm::Shm(QObject *parent)
|
||||
: QWaylandClientExtensionTemplate(::version)
|
||||
{
|
||||
setParent(parent);
|
||||
connect(this, &QWaylandClientExtension::activeChanged, this, [this] {
|
||||
if (!isActive()) {
|
||||
wl_shm_destroy(object());
|
||||
}
|
||||
});
|
||||
initialize();
|
||||
}
|
||||
|
||||
Shm *Shm::instance()
|
||||
{
|
||||
static Shm *instance = new Shm(qGuiApp);
|
||||
return instance;
|
||||
}
|
||||
|
||||
Shm::~Shm() noexcept
|
||||
{
|
||||
if (isActive()) {
|
||||
wl_shm_destroy(object());
|
||||
}
|
||||
}
|
||||
|
||||
static wl_shm_format toWaylandFormat(QImage::Format format)
|
||||
{
|
||||
switch (format) {
|
||||
case QImage::Format_ARGB32_Premultiplied:
|
||||
return WL_SHM_FORMAT_ARGB8888;
|
||||
case QImage::Format_RGB32:
|
||||
return WL_SHM_FORMAT_XRGB8888;
|
||||
case QImage::Format_ARGB32:
|
||||
qCWarning(KWAYLAND_KWS()) << "Unsupported image format: " << format << ". expect slow performance. Use QImage::Format_ARGB32_Premultiplied";
|
||||
return WL_SHM_FORMAT_ARGB8888;
|
||||
default:
|
||||
qCWarning(KWAYLAND_KWS()) << "Unsupported image format: " << format << ". expect slow performance.";
|
||||
return WL_SHM_FORMAT_ARGB8888;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ShmBuffer> Shm::createBuffer(const QImage &image)
|
||||
{
|
||||
if (image.isNull()) {
|
||||
return {};
|
||||
}
|
||||
auto format = toWaylandFormat(image.format());
|
||||
const int stride = image.bytesPerLine();
|
||||
const int32_t byteCount = image.size().height() * stride;
|
||||
|
||||
int fd = -1;
|
||||
#if defined HAVE_MEMFD
|
||||
fd = memfd_create("kwayland-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||
if (fd >= 0) {
|
||||
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
char templateName[] = "/tmp/kwayland-shared-XXXXXX";
|
||||
fd = mkstemp(templateName);
|
||||
if (fd >= 0) {
|
||||
unlink(templateName);
|
||||
|
||||
int flags = fcntl(fd, F_GETFD);
|
||||
if (flags == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fd == -1) {
|
||||
qCDebug(KWAYLAND_KWS) << "Could not open temporary file for Shm pool";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (ftruncate(fd, byteCount) < 0) {
|
||||
qCDebug(KWAYLAND_KWS) << "Could not set size for Shm pool file";
|
||||
close(fd);
|
||||
return {};
|
||||
}
|
||||
auto data = mmap(nullptr, byteCount, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
|
||||
if (data == MAP_FAILED) {
|
||||
qCDebug(KWAYLAND_KWS) << "Creating Shm pool failed";
|
||||
close(fd);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto pool = create_pool(fd, byteCount);
|
||||
auto *buffer = wl_shm_pool_create_buffer(pool, 0, image.size().width(), image.size().height(), stride, format);
|
||||
wl_shm_pool_destroy(pool);
|
||||
|
||||
const QImage &srcImage = [format, &image] {
|
||||
if (format == WL_SHM_FORMAT_ARGB8888 && image.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||
return image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
} else {
|
||||
return image;
|
||||
}
|
||||
}();
|
||||
|
||||
std::memcpy(static_cast<char *>(data), srcImage.bits(), byteCount);
|
||||
|
||||
munmap(data, byteCount);
|
||||
close(fd);
|
||||
return std::make_unique<ShmBuffer>(buffer);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2023 David Redondo <kde@david-redondo.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
#include <qwayland-wayland.h>
|
||||
|
||||
#include <QSize>
|
||||
#include <QWaylandClientExtensionTemplate>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class ShmBuffer : public QtWayland::wl_buffer
|
||||
{
|
||||
public:
|
||||
ShmBuffer(::wl_buffer *buffer);
|
||||
~ShmBuffer();
|
||||
};
|
||||
|
||||
class Shm : public QWaylandClientExtensionTemplate<Shm>, public QtWayland::wl_shm
|
||||
{
|
||||
public:
|
||||
static Shm *instance();
|
||||
~Shm();
|
||||
std::unique_ptr<ShmBuffer> createBuffer(const QImage &image);
|
||||
|
||||
private:
|
||||
Shm(QObject *parent);
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWindow>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
|
||||
struct wl_surface;
|
||||
|
||||
inline wl_surface *surfaceForWindow(QWindow *window)
|
||||
{
|
||||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QPlatformNativeInterface *native = qGuiApp->platformNativeInterface();
|
||||
if (!native) {
|
||||
return nullptr;
|
||||
}
|
||||
window->create();
|
||||
return reinterpret_cast<wl_surface *>(native->nativeResourceForWindow(QByteArrayLiteral("surface"), window));
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"platforms": ["wayland", "wayland-egl"]
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "waylandxdgactivationv1_p.h"
|
||||
#include <QGuiApplication>
|
||||
|
||||
WaylandXdgActivationV1::WaylandXdgActivationV1()
|
||||
: QWaylandClientExtensionTemplate<WaylandXdgActivationV1>(1)
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
|
||||
WaylandXdgActivationV1::~WaylandXdgActivationV1()
|
||||
{
|
||||
if (qGuiApp && isActive()) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
WaylandXdgActivationV1 *WaylandXdgActivationV1::self()
|
||||
{
|
||||
static WaylandXdgActivationV1 *instance = new WaylandXdgActivationV1;
|
||||
return instance;
|
||||
}
|
||||
|
||||
WaylandXdgActivationTokenV1 *
|
||||
WaylandXdgActivationV1::requestXdgActivationToken(wl_seat *seat, struct ::wl_surface *surface, uint32_t serial, const QString &app_id)
|
||||
{
|
||||
auto wl = get_activation_token();
|
||||
auto provider = new WaylandXdgActivationTokenV1;
|
||||
provider->init(wl);
|
||||
|
||||
if (surface)
|
||||
provider->set_surface(surface);
|
||||
|
||||
if (!app_id.isEmpty())
|
||||
provider->set_app_id(app_id);
|
||||
|
||||
if (seat)
|
||||
provider->set_serial(serial, seat);
|
||||
provider->commit();
|
||||
return provider;
|
||||
}
|
||||
|
||||
#include "moc_waylandxdgactivationv1_p.cpp"
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef WAYLANDXDGACTIVATIONV1_P_H
|
||||
#define WAYLANDXDGACTIVATIONV1_P_H
|
||||
|
||||
#include "qwayland-xdg-activation-v1.h"
|
||||
#include <QObject>
|
||||
#include <QtWaylandClient/QWaylandClientExtension>
|
||||
|
||||
class QWaylandSurface;
|
||||
|
||||
class WaylandXdgActivationTokenV1 : public QObject, public QtWayland::xdg_activation_token_v1
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
void xdg_activation_token_v1_done(const QString &token) override
|
||||
{
|
||||
Q_EMIT done(token);
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void failed();
|
||||
void done(const QString &token);
|
||||
};
|
||||
|
||||
class WaylandXdgActivationV1 : public QWaylandClientExtensionTemplate<WaylandXdgActivationV1>, public QtWayland::xdg_activation_v1
|
||||
{
|
||||
public:
|
||||
~WaylandXdgActivationV1() override;
|
||||
|
||||
static WaylandXdgActivationV1 *self();
|
||||
|
||||
WaylandXdgActivationTokenV1 *requestXdgActivationToken(wl_seat *seat, struct ::wl_surface *surface, uint32_t serial, const QString &app_id);
|
||||
|
||||
private:
|
||||
WaylandXdgActivationV1();
|
||||
};
|
||||
|
||||
#endif
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "waylandxdgforeignv2_p.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
|
||||
WaylandXdgForeignExportedV2::WaylandXdgForeignExportedV2(::zxdg_exported_v2 *object)
|
||||
: QObject()
|
||||
, QtWayland::zxdg_exported_v2(object)
|
||||
{
|
||||
}
|
||||
|
||||
WaylandXdgForeignExportedV2::~WaylandXdgForeignExportedV2()
|
||||
{
|
||||
if (qGuiApp) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
QString WaylandXdgForeignExportedV2::handle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
void WaylandXdgForeignExportedV2::zxdg_exported_v2_handle(const QString &handle)
|
||||
{
|
||||
m_handle = handle;
|
||||
Q_EMIT handleReceived(handle);
|
||||
}
|
||||
|
||||
WaylandXdgForeignExporterV2::WaylandXdgForeignExporterV2()
|
||||
: QWaylandClientExtensionTemplate<WaylandXdgForeignExporterV2>(1)
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
|
||||
WaylandXdgForeignExporterV2::~WaylandXdgForeignExporterV2()
|
||||
{
|
||||
if (qGuiApp && isActive()) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
WaylandXdgForeignExporterV2 &WaylandXdgForeignExporterV2::self()
|
||||
{
|
||||
static WaylandXdgForeignExporterV2 s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
WaylandXdgForeignExportedV2 *WaylandXdgForeignExporterV2::exportToplevel(wl_surface *surface)
|
||||
{
|
||||
return new WaylandXdgForeignExportedV2(export_toplevel(surface));
|
||||
}
|
||||
|
||||
WaylandXdgForeignImportedV2::WaylandXdgForeignImportedV2(const QString &handle, ::zxdg_imported_v2 *object)
|
||||
: QObject()
|
||||
, QtWayland::zxdg_imported_v2(object)
|
||||
, m_handle(handle)
|
||||
{
|
||||
}
|
||||
|
||||
WaylandXdgForeignImportedV2::~WaylandXdgForeignImportedV2()
|
||||
{
|
||||
if (qGuiApp) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandXdgForeignImportedV2::zxdg_imported_v2_destroyed()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
QString WaylandXdgForeignImportedV2::handle() const
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
WaylandXdgForeignImporterV2::WaylandXdgForeignImporterV2()
|
||||
: QWaylandClientExtensionTemplate<WaylandXdgForeignImporterV2>(1)
|
||||
{
|
||||
initialize();
|
||||
}
|
||||
|
||||
WaylandXdgForeignImporterV2::~WaylandXdgForeignImporterV2()
|
||||
{
|
||||
if (qGuiApp && isActive()) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
WaylandXdgForeignImporterV2 &WaylandXdgForeignImporterV2::self()
|
||||
{
|
||||
static WaylandXdgForeignImporterV2 s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
WaylandXdgForeignImportedV2 *WaylandXdgForeignImporterV2::importToplevel(const QString &handle)
|
||||
{
|
||||
return new WaylandXdgForeignImportedV2(handle, import_toplevel(handle));
|
||||
}
|
||||
|
||||
#include "moc_waylandxdgforeignv2_p.cpp"
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef WAYLANDXDGFOREIGNV2_P_H
|
||||
#define WAYLANDXDGFOREIGNV2_P_H
|
||||
|
||||
#include "qwayland-xdg-foreign-unstable-v2.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QtWaylandClient/QWaylandClientExtension>
|
||||
|
||||
class WaylandXdgForeignExportedV2 : public QObject, public QtWayland::zxdg_exported_v2
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit WaylandXdgForeignExportedV2(::zxdg_exported_v2 *object);
|
||||
~WaylandXdgForeignExportedV2() override;
|
||||
|
||||
QString handle() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void handleReceived(const QString &handle);
|
||||
|
||||
protected:
|
||||
void zxdg_exported_v2_handle(const QString &handle) override;
|
||||
|
||||
private:
|
||||
QString m_handle;
|
||||
};
|
||||
|
||||
class WaylandXdgForeignExporterV2 : public QWaylandClientExtensionTemplate<WaylandXdgForeignExporterV2>, public QtWayland::zxdg_exporter_v2
|
||||
{
|
||||
public:
|
||||
~WaylandXdgForeignExporterV2() override;
|
||||
|
||||
static WaylandXdgForeignExporterV2 &self();
|
||||
|
||||
WaylandXdgForeignExportedV2 *exportToplevel(struct ::wl_surface *surface);
|
||||
|
||||
private:
|
||||
WaylandXdgForeignExporterV2();
|
||||
};
|
||||
|
||||
class WaylandXdgForeignImportedV2 : public QObject, public QtWayland::zxdg_imported_v2
|
||||
{
|
||||
public:
|
||||
explicit WaylandXdgForeignImportedV2(const QString &handle, ::zxdg_imported_v2 *object);
|
||||
~WaylandXdgForeignImportedV2() override;
|
||||
|
||||
QString handle() const;
|
||||
|
||||
protected:
|
||||
void zxdg_imported_v2_destroyed() override;
|
||||
|
||||
private:
|
||||
QString m_handle;
|
||||
};
|
||||
|
||||
class WaylandXdgForeignImporterV2 : public QWaylandClientExtensionTemplate<WaylandXdgForeignImporterV2>, public QtWayland::zxdg_importer_v2
|
||||
{
|
||||
public:
|
||||
~WaylandXdgForeignImporterV2() override;
|
||||
|
||||
static WaylandXdgForeignImporterV2 &self();
|
||||
|
||||
WaylandXdgForeignImportedV2 *importToplevel(const QString &handle);
|
||||
|
||||
private:
|
||||
WaylandXdgForeignImporterV2();
|
||||
};
|
||||
|
||||
#endif // WAYLANDXDGFOREIGNV2_P_H
|
||||
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "windoweffects.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QExposeEvent>
|
||||
#include <QGuiApplication>
|
||||
|
||||
#include <qpa/qplatformwindow_p.h>
|
||||
|
||||
#include <QWaylandClientExtensionTemplate>
|
||||
#include <qwaylandclientextension.h>
|
||||
|
||||
#include "qwayland-blur.h"
|
||||
#include "qwayland-contrast.h"
|
||||
#include "qwayland-slide.h"
|
||||
|
||||
#include "surfacehelper.h"
|
||||
|
||||
#include <wayland-client-protocol.h>
|
||||
|
||||
static wl_region *createRegion(const QRegion ®ion)
|
||||
{
|
||||
QPlatformNativeInterface *native = qGuiApp->platformNativeInterface();
|
||||
if (!native) {
|
||||
return nullptr;
|
||||
}
|
||||
auto compositor = reinterpret_cast<wl_compositor *>(native->nativeResourceForIntegration(QByteArrayLiteral("compositor")));
|
||||
if (!compositor) {
|
||||
return nullptr;
|
||||
}
|
||||
auto wl_region = wl_compositor_create_region(compositor);
|
||||
for (const auto &rect : region) {
|
||||
wl_region_add(wl_region, rect.x(), rect.y(), rect.width(), rect.height());
|
||||
}
|
||||
return wl_region;
|
||||
}
|
||||
|
||||
class BlurManager : public QWaylandClientExtensionTemplate<BlurManager>, public QtWayland::org_kde_kwin_blur_manager
|
||||
{
|
||||
public:
|
||||
BlurManager()
|
||||
: QWaylandClientExtensionTemplate<BlurManager>(1)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Blur : public QObject, public QtWayland::org_kde_kwin_blur
|
||||
{
|
||||
public:
|
||||
Blur(struct ::org_kde_kwin_blur *object, QObject *parent)
|
||||
: QObject(parent)
|
||||
, QtWayland::org_kde_kwin_blur(object)
|
||||
{
|
||||
}
|
||||
|
||||
~Blur() override
|
||||
{
|
||||
release();
|
||||
}
|
||||
};
|
||||
|
||||
class ContrastManager : public QWaylandClientExtensionTemplate<ContrastManager>, public QtWayland::org_kde_kwin_contrast_manager
|
||||
{
|
||||
public:
|
||||
ContrastManager()
|
||||
: QWaylandClientExtensionTemplate<ContrastManager>(2)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Contrast : public QObject, public QtWayland::org_kde_kwin_contrast
|
||||
{
|
||||
public:
|
||||
Contrast(struct ::org_kde_kwin_contrast *object, QObject *parent)
|
||||
: QObject(parent)
|
||||
, QtWayland::org_kde_kwin_contrast(object)
|
||||
{
|
||||
}
|
||||
|
||||
~Contrast() override
|
||||
{
|
||||
release();
|
||||
}
|
||||
};
|
||||
|
||||
class SlideManager : public QWaylandClientExtensionTemplate<SlideManager>, public QtWayland::org_kde_kwin_slide_manager
|
||||
{
|
||||
public:
|
||||
SlideManager()
|
||||
: QWaylandClientExtensionTemplate<SlideManager>(1)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Slide : public QObject, public QtWayland::org_kde_kwin_slide
|
||||
{
|
||||
public:
|
||||
Slide(struct ::org_kde_kwin_slide *object, QObject *parent)
|
||||
: QObject(parent)
|
||||
, QtWayland::org_kde_kwin_slide(object)
|
||||
{
|
||||
}
|
||||
|
||||
~Slide() override
|
||||
{
|
||||
release();
|
||||
}
|
||||
};
|
||||
|
||||
WindowEffects::WindowEffects()
|
||||
: QObject()
|
||||
, KWindowEffectsPrivate()
|
||||
{
|
||||
m_blurManager = new BlurManager();
|
||||
m_contrastManager = new ContrastManager();
|
||||
m_slideManager = new SlideManager();
|
||||
|
||||
// The KWindowEffects API doesn't provide any signals to notify that the particular
|
||||
// effect has become unavailable. So we re-install effects when the corresponding globals
|
||||
// are added.
|
||||
|
||||
connect(m_blurManager, &BlurManager::activeChanged, this, [this] {
|
||||
for (auto it = m_blurRegions.constBegin(); it != m_blurRegions.constEnd(); ++it) {
|
||||
installBlur(it.key(), m_blurManager->isActive(), *it);
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_contrastManager, &ContrastManager::activeChanged, this, [this] {
|
||||
for (auto it = m_backgroundConstrastRegions.constBegin(); it != m_backgroundConstrastRegions.constEnd(); ++it) {
|
||||
if (m_contrastManager->isActive()) {
|
||||
installContrast(it.key(), true, it->contrast, it->intensity, it->saturation, it->region);
|
||||
} else {
|
||||
installContrast(it.key(), false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_slideManager, &SlideManager::activeChanged, this, [this] {
|
||||
for (auto it = m_slideMap.constBegin(); it != m_slideMap.constEnd(); ++it) {
|
||||
if (m_slideManager->isActive()) {
|
||||
installSlide(it.key(), it->location, it->offset);
|
||||
} else {
|
||||
installSlide(it.key(), KWindowEffects::SlideFromLocation::NoEdge, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
WindowEffects::~WindowEffects()
|
||||
{
|
||||
delete m_blurManager;
|
||||
delete m_contrastManager;
|
||||
delete m_slideManager;
|
||||
}
|
||||
|
||||
void WindowEffects::trackWindow(QWindow *window)
|
||||
{
|
||||
if (!m_windowWatchers.contains(window)) {
|
||||
window->installEventFilter(this);
|
||||
auto conn = connect(window, &QObject::destroyed, this, [this, window]() {
|
||||
resetBlur(window);
|
||||
m_blurRegions.remove(window);
|
||||
resetContrast(window);
|
||||
m_backgroundConstrastRegions.remove(window);
|
||||
m_slideMap.remove(window);
|
||||
m_windowWatchers.remove(window);
|
||||
});
|
||||
m_windowWatchers[window] << conn;
|
||||
auto waylandWindow = window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
|
||||
if (waylandWindow) {
|
||||
auto conn = connect(waylandWindow, &QNativeInterface::Private::QWaylandWindow::surfaceDestroyed, this, [this, window]() {
|
||||
resetBlur(window);
|
||||
resetContrast(window);
|
||||
});
|
||||
m_windowWatchers[window] << conn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WindowEffects::releaseWindow(QWindow *window)
|
||||
{
|
||||
if (!m_blurRegions.contains(window) && !m_backgroundConstrastRegions.contains(window) && !m_slideMap.contains(window)) {
|
||||
for (const auto &conn : m_windowWatchers[window]) {
|
||||
disconnect(conn);
|
||||
}
|
||||
window->removeEventFilter(this);
|
||||
m_windowWatchers.remove(window);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to replace a QObject value in the map and delete the old one.
|
||||
template<typename MapType>
|
||||
void replaceValue(MapType &map, typename MapType::key_type key, typename MapType::mapped_type value)
|
||||
{
|
||||
if (auto oldValue = map.take(key)) {
|
||||
oldValue->deleteLater();
|
||||
}
|
||||
if (value) {
|
||||
map[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void WindowEffects::resetBlur(QWindow *window, Blur *blur)
|
||||
{
|
||||
replaceValue(m_blurs, window, blur);
|
||||
}
|
||||
|
||||
void WindowEffects::resetContrast(QWindow *window, Contrast *contrast)
|
||||
{
|
||||
replaceValue(m_contrasts, window, contrast);
|
||||
}
|
||||
|
||||
bool WindowEffects::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::Expose) {
|
||||
auto window = qobject_cast<QWindow *>(watched);
|
||||
if (!window || !window->isExposed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
auto it = m_blurRegions.constFind(window);
|
||||
if (it != m_blurRegions.constEnd()) {
|
||||
installBlur(window, true, *it);
|
||||
}
|
||||
}
|
||||
{
|
||||
auto it = m_backgroundConstrastRegions.constFind(window);
|
||||
if (it != m_backgroundConstrastRegions.constEnd()) {
|
||||
installContrast(window, true, it->contrast, it->intensity, it->saturation, it->region);
|
||||
}
|
||||
}
|
||||
{
|
||||
auto it = m_slideMap.constFind(window);
|
||||
if (it != m_slideMap.constEnd()) {
|
||||
installSlide(window, it->location, it->offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WindowEffects::isEffectAvailable(KWindowEffects::Effect effect)
|
||||
{
|
||||
switch (effect) {
|
||||
case KWindowEffects::BackgroundContrast:
|
||||
return m_contrastManager->isActive();
|
||||
case KWindowEffects::BlurBehind:
|
||||
return m_blurManager->isActive();
|
||||
case KWindowEffects::Slide:
|
||||
return m_slideManager->isActive();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void WindowEffects::slideWindow(QWindow *window, KWindowEffects::SlideFromLocation location, int offset)
|
||||
{
|
||||
if (location != KWindowEffects::SlideFromLocation::NoEdge) {
|
||||
m_slideMap[window] = SlideData{
|
||||
.location = location,
|
||||
.offset = offset,
|
||||
};
|
||||
trackWindow(window);
|
||||
} else {
|
||||
m_slideMap.remove(window);
|
||||
releaseWindow(window);
|
||||
}
|
||||
|
||||
installSlide(window, location, offset);
|
||||
}
|
||||
|
||||
void WindowEffects::installSlide(QWindow *window, KWindowEffects::SlideFromLocation location, int offset)
|
||||
{
|
||||
if (!m_slideManager->isActive()) {
|
||||
return;
|
||||
}
|
||||
wl_surface *surface = surfaceForWindow(window);
|
||||
if (surface) {
|
||||
if (location != KWindowEffects::SlideFromLocation::NoEdge) {
|
||||
auto slide = new Slide(m_slideManager->create(surface), window);
|
||||
|
||||
Slide::location convertedLoc;
|
||||
switch (location) {
|
||||
case KWindowEffects::SlideFromLocation::TopEdge:
|
||||
convertedLoc = Slide::location::location_top;
|
||||
break;
|
||||
case KWindowEffects::SlideFromLocation::LeftEdge:
|
||||
convertedLoc = Slide::location::location_left;
|
||||
break;
|
||||
case KWindowEffects::SlideFromLocation::RightEdge:
|
||||
convertedLoc = Slide::location::location_right;
|
||||
break;
|
||||
case KWindowEffects::SlideFromLocation::BottomEdge:
|
||||
default:
|
||||
convertedLoc = Slide::location::location_bottom;
|
||||
break;
|
||||
}
|
||||
|
||||
slide->set_location(convertedLoc);
|
||||
slide->set_offset(offset);
|
||||
slide->commit();
|
||||
} else {
|
||||
m_slideManager->unset(surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WindowEffects::enableBlurBehind(QWindow *window, bool enable, const QRegion ®ion)
|
||||
{
|
||||
if (enable) {
|
||||
trackWindow(window);
|
||||
m_blurRegions[window] = region;
|
||||
} else {
|
||||
resetBlur(window);
|
||||
m_blurRegions.remove(window);
|
||||
releaseWindow(window);
|
||||
}
|
||||
|
||||
installBlur(window, enable, region);
|
||||
}
|
||||
|
||||
void WindowEffects::installBlur(QWindow *window, bool enable, const QRegion ®ion)
|
||||
{
|
||||
if (!m_blurManager->isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
wl_surface *surface = surfaceForWindow(window);
|
||||
|
||||
if (surface) {
|
||||
if (enable) {
|
||||
auto wl_region = createRegion(region);
|
||||
if (!wl_region) {
|
||||
return;
|
||||
}
|
||||
auto blur = new Blur(m_blurManager->create(surface), window);
|
||||
blur->set_region(wl_region);
|
||||
blur->commit();
|
||||
wl_region_destroy(wl_region);
|
||||
resetBlur(window, blur);
|
||||
} else {
|
||||
resetBlur(window);
|
||||
m_blurManager->unset(surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WindowEffects::enableBackgroundContrast(QWindow *window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion ®ion)
|
||||
{
|
||||
if (enable) {
|
||||
trackWindow(window);
|
||||
m_backgroundConstrastRegions[window].contrast = contrast;
|
||||
m_backgroundConstrastRegions[window].intensity = intensity;
|
||||
m_backgroundConstrastRegions[window].saturation = saturation;
|
||||
m_backgroundConstrastRegions[window].region = region;
|
||||
} else {
|
||||
resetContrast(window);
|
||||
m_backgroundConstrastRegions.remove(window);
|
||||
releaseWindow(window);
|
||||
}
|
||||
|
||||
installContrast(window, enable, contrast, intensity, saturation, region);
|
||||
}
|
||||
|
||||
void WindowEffects::installContrast(QWindow *window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion ®ion)
|
||||
{
|
||||
if (!m_contrastManager->isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
wl_surface *surface = surfaceForWindow(window);
|
||||
if (surface) {
|
||||
if (enable) {
|
||||
auto wl_region = createRegion(region);
|
||||
if (!wl_region) {
|
||||
return;
|
||||
}
|
||||
auto backgroundContrast = new Contrast(m_contrastManager->create(surface), window);
|
||||
backgroundContrast->set_region(wl_region);
|
||||
backgroundContrast->set_contrast(wl_fixed_from_double(contrast));
|
||||
backgroundContrast->set_intensity(wl_fixed_from_double(intensity));
|
||||
backgroundContrast->set_saturation(wl_fixed_from_double(saturation));
|
||||
backgroundContrast->commit();
|
||||
wl_region_destroy(wl_region);
|
||||
resetContrast(window, backgroundContrast);
|
||||
} else {
|
||||
resetContrast(window);
|
||||
m_contrastManager->unset(surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_windoweffects.cpp"
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#ifndef WINDOWEFFECTS_H
|
||||
#define WINDOWEFFECTS_H
|
||||
#include "kwindoweffects_p.h"
|
||||
#include <kwindowsystem_version.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
|
||||
class BlurManager;
|
||||
class Blur;
|
||||
class ContrastManager;
|
||||
class Contrast;
|
||||
class SlideManager;
|
||||
|
||||
class WindowEffects : public QObject, public KWindowEffectsPrivate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WindowEffects();
|
||||
~WindowEffects() override;
|
||||
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
void trackWindow(QWindow *window);
|
||||
void releaseWindow(QWindow *window);
|
||||
|
||||
bool isEffectAvailable(KWindowEffects::Effect effect) override;
|
||||
void slideWindow(QWindow *window, KWindowEffects::SlideFromLocation location, int offset) override;
|
||||
void enableBlurBehind(QWindow *window, bool enable = true, const QRegion ®ion = QRegion()) override;
|
||||
void enableBackgroundContrast(QWindow *window,
|
||||
bool enable = true,
|
||||
qreal contrast = 1,
|
||||
qreal intensity = 1,
|
||||
qreal saturation = 1,
|
||||
const QRegion ®ion = QRegion()) override;
|
||||
|
||||
private:
|
||||
void installContrast(QWindow *window, bool enable = true, qreal contrast = 1, qreal intensity = 1, qreal saturation = 1, const QRegion ®ion = QRegion());
|
||||
void installBlur(QWindow *window, bool enable, const QRegion ®ion);
|
||||
void installSlide(QWindow *window, KWindowEffects::SlideFromLocation location, int offset);
|
||||
|
||||
void resetBlur(QWindow *window, Blur *blur = nullptr);
|
||||
void resetContrast(QWindow *window, Contrast *contrast = nullptr);
|
||||
|
||||
QHash<QWindow *, QList<QMetaObject::Connection>> m_windowWatchers;
|
||||
QHash<QWindow *, QRegion> m_blurRegions;
|
||||
struct BackgroundContrastData {
|
||||
qreal contrast = 1;
|
||||
qreal intensity = 1;
|
||||
qreal saturation = 1;
|
||||
QRegion region;
|
||||
};
|
||||
QHash<QWindow *, BackgroundContrastData> m_backgroundConstrastRegions;
|
||||
QHash<QWindow *, QPointer<Blur>> m_blurs;
|
||||
QHash<QWindow *, QPointer<Contrast>> m_contrasts;
|
||||
struct SlideData {
|
||||
KWindowEffects::SlideFromLocation location;
|
||||
int offset;
|
||||
};
|
||||
QHash<QWindow *, SlideData> m_slideMap;
|
||||
BlurManager *m_blurManager;
|
||||
ContrastManager *m_contrastManager;
|
||||
SlideManager *m_slideManager;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
SPDX-FileCopyrightText: 2023 David Redondo <kde@david-redondo.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "windowshadow.h"
|
||||
#include "logging.h"
|
||||
#include "shm.h"
|
||||
#include "surfacehelper.h"
|
||||
|
||||
#include <qwayland-shadow.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QExposeEvent>
|
||||
#include <QWaylandClientExtension>
|
||||
|
||||
#include <qpa/qplatformwindow_p.h>
|
||||
|
||||
class ShadowManager : public QWaylandClientExtensionTemplate<ShadowManager>, public QtWayland::org_kde_kwin_shadow_manager
|
||||
{
|
||||
Q_OBJECT
|
||||
static constexpr int version = 2;
|
||||
explicit ShadowManager(QObject *parent = nullptr)
|
||||
: QWaylandClientExtensionTemplate(version)
|
||||
{
|
||||
setParent(parent);
|
||||
initialize();
|
||||
|
||||
connect(this, &QWaylandClientExtension::activeChanged, this, [this] {
|
||||
if (!isActive()) {
|
||||
destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
~ShadowManager()
|
||||
{
|
||||
if (isActive()) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
static ShadowManager *instance()
|
||||
{
|
||||
static ShadowManager *instance = new ShadowManager(qGuiApp);
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
class Shadow : public QtWayland::org_kde_kwin_shadow
|
||||
{
|
||||
public:
|
||||
using QtWayland::org_kde_kwin_shadow::org_kde_kwin_shadow;
|
||||
~Shadow()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
};
|
||||
|
||||
WindowShadowTile::WindowShadowTile()
|
||||
{
|
||||
connect(Shm::instance(), &Shm::activeChanged, this, [this] {
|
||||
if (Shm::instance()->isActive()) {
|
||||
buffer.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
WindowShadowTile::~WindowShadowTile()
|
||||
{
|
||||
}
|
||||
|
||||
bool WindowShadowTile::create()
|
||||
{
|
||||
if (!Shm::instance()->isActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer = Shm::instance()->createBuffer(image);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowShadowTile::destroy()
|
||||
{
|
||||
buffer.reset();
|
||||
}
|
||||
|
||||
WindowShadowTile *WindowShadowTile::get(const KWindowShadowTile *tile)
|
||||
{
|
||||
KWindowShadowTilePrivate *d = KWindowShadowTilePrivate::get(tile);
|
||||
return static_cast<WindowShadowTile *>(d);
|
||||
}
|
||||
|
||||
static wl_buffer *bufferForTile(const KWindowShadowTile::Ptr &tile)
|
||||
{
|
||||
if (!tile) {
|
||||
return nullptr;
|
||||
}
|
||||
WindowShadowTile *d = WindowShadowTile::get(tile.data());
|
||||
// Our buffer has been deleted in the meantime, try to create it again
|
||||
if (!d->buffer && d->isCreated) {
|
||||
d->buffer = Shm::instance()->createBuffer(d->image);
|
||||
}
|
||||
return d->buffer ? d->buffer.get()->object() : nullptr;
|
||||
}
|
||||
|
||||
WindowShadow::WindowShadow()
|
||||
{
|
||||
}
|
||||
WindowShadow::~WindowShadow()
|
||||
{
|
||||
}
|
||||
|
||||
bool WindowShadow::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
Q_UNUSED(watched)
|
||||
if (event->type() == QEvent::Expose) {
|
||||
if (auto window = qobject_cast<QWindow *>(watched); window && window->isExposed()) {
|
||||
if (!internalCreate()) {
|
||||
qCWarning(KWAYLAND_KWS) << "Failed to recreate shadow for" << window;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WindowShadow::internalCreate()
|
||||
{
|
||||
if (shadow) {
|
||||
return true;
|
||||
}
|
||||
if (!ShadowManager::instance()->isActive()) {
|
||||
return false;
|
||||
}
|
||||
auto surface = surfaceForWindow(window);
|
||||
if (!surface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
shadow = std::make_unique<Shadow>(ShadowManager::instance()->create(surface));
|
||||
auto waylandWindow = window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
|
||||
if (waylandWindow) {
|
||||
connect(waylandWindow, &QNativeInterface::Private::QWaylandWindow::surfaceDestroyed, this, &WindowShadow::internalDestroy, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
auto attach = [](const std::unique_ptr<Shadow> &shadow, auto attach_func, const KWindowShadowTile::Ptr &tile) {
|
||||
if (auto buffer = bufferForTile(tile)) {
|
||||
(*shadow.*attach_func)(buffer);
|
||||
}
|
||||
};
|
||||
attach(shadow, &Shadow::attach_left, leftTile);
|
||||
attach(shadow, &Shadow::attach_top_left, topLeftTile);
|
||||
attach(shadow, &Shadow::attach_top, topTile);
|
||||
attach(shadow, &Shadow::attach_top_right, topRightTile);
|
||||
attach(shadow, &Shadow::attach_right, rightTile);
|
||||
attach(shadow, &Shadow::attach_bottom_right, bottomRightTile);
|
||||
attach(shadow, &Shadow::attach_bottom, bottomTile);
|
||||
attach(shadow, &Shadow::attach_bottom_left, bottomLeftTile);
|
||||
|
||||
shadow->set_left_offset(wl_fixed_from_double(padding.left()));
|
||||
shadow->set_top_offset(wl_fixed_from_double(padding.top()));
|
||||
shadow->set_right_offset(wl_fixed_from_double(padding.right()));
|
||||
shadow->set_bottom_offset(wl_fixed_from_double(padding.bottom()));
|
||||
|
||||
shadow->commit();
|
||||
|
||||
// Commit wl_surface at the next available time.
|
||||
window->requestUpdate();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowShadow::create()
|
||||
{
|
||||
if (!ShadowManager::instance()->isActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
internalCreate();
|
||||
window->installEventFilter(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowShadow::internalDestroy()
|
||||
{
|
||||
if (!shadow) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only call surfaceForWindow and unset the surface if the native window is alive.
|
||||
// Otherwise window->create() might be called when the window is being destroyed, leading to a crash.
|
||||
if (window && window->nativeInterface<QNativeInterface::Private::QWaylandWindow>() && ShadowManager::instance()->isActive()) {
|
||||
if (auto surface = surfaceForWindow(window)) {
|
||||
ShadowManager::instance()->unset(surface);
|
||||
}
|
||||
}
|
||||
|
||||
shadow.reset();
|
||||
|
||||
if (window && window->isVisible()) {
|
||||
window->requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowShadow::destroy()
|
||||
{
|
||||
if (window) {
|
||||
window->removeEventFilter(this);
|
||||
}
|
||||
internalDestroy();
|
||||
}
|
||||
|
||||
#include "windowshadow.moc"
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef WINDOWSHADOW_H
|
||||
#define WINDOWSHADOW_H
|
||||
|
||||
#include "kwindowshadow_p.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Shadow;
|
||||
class ShmBuffer;
|
||||
class Shm;
|
||||
|
||||
class WindowShadowTile final : public QObject, public KWindowShadowTilePrivate
|
||||
{
|
||||
public:
|
||||
WindowShadowTile();
|
||||
~WindowShadowTile();
|
||||
|
||||
bool create() override;
|
||||
void destroy() override;
|
||||
|
||||
static WindowShadowTile *get(const KWindowShadowTile *tile);
|
||||
|
||||
std::unique_ptr<ShmBuffer> buffer;
|
||||
};
|
||||
|
||||
class WindowShadow final : public QObject, public KWindowShadowPrivate
|
||||
{
|
||||
public:
|
||||
WindowShadow();
|
||||
~WindowShadow() override;
|
||||
bool create() override;
|
||||
void destroy() override;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
private:
|
||||
bool internalCreate();
|
||||
void internalDestroy();
|
||||
|
||||
std::unique_ptr<Shadow> shadow;
|
||||
};
|
||||
|
||||
#endif // WINDOWSHADOW_H
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "windowsystem.h"
|
||||
#include "logging.h"
|
||||
#include "surfacehelper.h"
|
||||
#include "waylandxdgactivationv1_p.h"
|
||||
#include "waylandxdgforeignv2_p.h"
|
||||
|
||||
#include <KWaylandExtras>
|
||||
#include <KWindowSystem>
|
||||
|
||||
#include "qwayland-plasma-window-management.h"
|
||||
#include <QEvent>
|
||||
#include <QGuiApplication>
|
||||
#include <QPixmap>
|
||||
#include <QPoint>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <QWaylandClientExtensionTemplate>
|
||||
#include <QWindow>
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#include <qpa/qplatformwindow_p.h>
|
||||
|
||||
constexpr const char *c_kdeXdgForeignExportedProperty("_kde_xdg_foreign_exported_v2");
|
||||
constexpr const char *c_kdeXdgForeignImportedProperty("_kde_xdg_foreign_imported_v2");
|
||||
constexpr const char *c_kdeXdgForeignPendingHandleProperty("_kde_xdg_foreign_pending_handle");
|
||||
|
||||
class WindowManagement : public QWaylandClientExtensionTemplate<WindowManagement>, public QtWayland::org_kde_plasma_window_management
|
||||
{
|
||||
public:
|
||||
WindowManagement()
|
||||
: QWaylandClientExtensionTemplate<WindowManagement>(17)
|
||||
{
|
||||
}
|
||||
|
||||
void org_kde_plasma_window_management_show_desktop_changed(uint32_t state) override
|
||||
{
|
||||
showingDesktop = state == show_desktop_enabled;
|
||||
KWindowSystem::self()->showingDesktopChanged(showingDesktop);
|
||||
}
|
||||
|
||||
bool showingDesktop = false;
|
||||
};
|
||||
|
||||
WindowSystem::WindowSystem()
|
||||
: QObject()
|
||||
, KWindowSystemPrivateV2()
|
||||
, m_lastToken(qEnvironmentVariable("XDG_ACTIVATION_TOKEN"))
|
||||
{
|
||||
m_windowManagement = new WindowManagement;
|
||||
}
|
||||
|
||||
WindowSystem::~WindowSystem()
|
||||
{
|
||||
delete m_windowManagement;
|
||||
}
|
||||
|
||||
void WindowSystem::activateWindow(QWindow *win, long int time)
|
||||
{
|
||||
Q_UNUSED(time);
|
||||
auto s = surfaceForWindow(win);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
WaylandXdgActivationV1 *activation = WaylandXdgActivationV1::self();
|
||||
if (!activation->isActive()) {
|
||||
return;
|
||||
}
|
||||
activation->activate(m_lastToken, s);
|
||||
}
|
||||
|
||||
void WindowSystem::requestToken(QWindow *window, uint32_t serial, const QString &app_id)
|
||||
{
|
||||
if (window) {
|
||||
window->create();
|
||||
}
|
||||
wl_surface *wlSurface = surfaceForWindow(window);
|
||||
|
||||
WaylandXdgActivationV1 *activation = WaylandXdgActivationV1::self();
|
||||
if (!activation->isActive()) {
|
||||
// Ensure that xdgActivationTokenArrived is always emitted asynchronously
|
||||
QTimer::singleShot(0, [serial] {
|
||||
Q_EMIT KWaylandExtras::self()->xdgActivationTokenArrived(serial, {});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
|
||||
auto seat = waylandApp ? waylandApp->lastInputSeat() : nullptr;
|
||||
auto tokenReq = activation->requestXdgActivationToken(seat, wlSurface, serial, app_id);
|
||||
connect(tokenReq, &WaylandXdgActivationTokenV1::failed, KWindowSystem::self(), [serial, app_id]() {
|
||||
Q_EMIT KWaylandExtras::self()->xdgActivationTokenArrived(serial, {});
|
||||
});
|
||||
connect(tokenReq, &WaylandXdgActivationTokenV1::done, KWindowSystem::self(), [serial](const QString &token) {
|
||||
Q_EMIT KWaylandExtras::self()->xdgActivationTokenArrived(serial, token);
|
||||
});
|
||||
}
|
||||
|
||||
void WindowSystem::setCurrentToken(const QString &token)
|
||||
{
|
||||
m_lastToken = token;
|
||||
}
|
||||
|
||||
quint32 WindowSystem::lastInputSerial(QWindow *window)
|
||||
{
|
||||
Q_UNUSED(window)
|
||||
if (auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>()) {
|
||||
return waylandApp->lastInputSerial();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WindowSystem::setShowingDesktop(bool showing)
|
||||
{
|
||||
if (!m_windowManagement->isActive()) {
|
||||
return;
|
||||
}
|
||||
m_windowManagement->show_desktop(showing ? WindowManagement::show_desktop_enabled : WindowManagement::show_desktop_disabled);
|
||||
}
|
||||
|
||||
bool WindowSystem::showingDesktop()
|
||||
{
|
||||
if (!m_windowManagement->isActive()) {
|
||||
return false;
|
||||
}
|
||||
return m_windowManagement->showingDesktop;
|
||||
}
|
||||
|
||||
void WindowSystem::exportWindow(QWindow *window)
|
||||
{
|
||||
auto emitHandle = [window](const QString &handle) {
|
||||
// Ensure that windowExported is always emitted asynchronously.
|
||||
QMetaObject::invokeMethod(
|
||||
window,
|
||||
[window, handle] {
|
||||
Q_EMIT KWaylandExtras::self()->windowExported(window, handle);
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
};
|
||||
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
window->create();
|
||||
|
||||
auto waylandWindow = window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
|
||||
if (!waylandWindow) {
|
||||
emitHandle({});
|
||||
return;
|
||||
}
|
||||
|
||||
auto &exporter = WaylandXdgForeignExporterV2::self();
|
||||
if (!exporter.isActive()) {
|
||||
emitHandle({});
|
||||
return;
|
||||
}
|
||||
|
||||
// We want to use QObject::property(char*) and use dynamic properties on the object rather than
|
||||
// call QWaylandWindow::property(QString) and send it around.
|
||||
WaylandXdgForeignExportedV2 *exported = waylandWindow->property(c_kdeXdgForeignExportedProperty).value<WaylandXdgForeignExportedV2 *>();
|
||||
if (!exported) {
|
||||
exported = exporter.exportToplevel(surfaceForWindow(window));
|
||||
exported->setParent(waylandWindow);
|
||||
|
||||
waylandWindow->setProperty(c_kdeXdgForeignExportedProperty, QVariant::fromValue(exported));
|
||||
connect(exported, &QObject::destroyed, waylandWindow, [waylandWindow] {
|
||||
waylandWindow->setProperty(c_kdeXdgForeignExportedProperty, QVariant());
|
||||
});
|
||||
|
||||
connect(exported, &WaylandXdgForeignExportedV2::handleReceived, window, [window](const QString &handle) {
|
||||
Q_EMIT KWaylandExtras::self()->windowExported(window, handle);
|
||||
});
|
||||
}
|
||||
|
||||
if (!exported->handle().isEmpty()) {
|
||||
emitHandle(exported->handle());
|
||||
}
|
||||
}
|
||||
|
||||
void WindowSystem::unexportWindow(QWindow *window)
|
||||
{
|
||||
auto waylandWindow = window ? window->nativeInterface<QNativeInterface::Private::QWaylandWindow>() : nullptr;
|
||||
if (!waylandWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
WaylandXdgForeignExportedV2 *exported = waylandWindow->property(c_kdeXdgForeignExportedProperty).value<WaylandXdgForeignExportedV2 *>();
|
||||
delete exported;
|
||||
Q_ASSERT(!waylandWindow->property(c_kdeXdgForeignExportedProperty).isValid());
|
||||
}
|
||||
|
||||
void WindowSystem::setMainWindow(QWindow *window, const QString &handle)
|
||||
{
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
window->create();
|
||||
auto waylandWindow = window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
|
||||
if (!waylandWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We want to use QObject::property(char*) and use dynamic properties on the object rather than
|
||||
// call QWaylandWindow::property(QString) and send it around.
|
||||
auto *imported = waylandWindow->property(c_kdeXdgForeignImportedProperty).value<WaylandXdgForeignImportedV2 *>();
|
||||
// Window already parented with a different handle? Delete imported so we import the new one later.
|
||||
if (imported && imported->handle() != handle) {
|
||||
delete imported;
|
||||
imported = nullptr;
|
||||
Q_ASSERT(!waylandWindow->property(c_kdeXdgForeignImportedProperty).isValid());
|
||||
}
|
||||
|
||||
// Don't bother.
|
||||
if (handle.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (window->isExposed()) {
|
||||
doSetMainWindow(window, handle);
|
||||
} else {
|
||||
// We can only import an XDG toplevel.
|
||||
// QWaylandWindow::surfaceRoleCreated is only in Qt 6.8,
|
||||
// in earlier versions wait for the window be exposed,
|
||||
// since QWaylandWindow::wlSurfaceCreated is too early.
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0)
|
||||
window->setProperty(c_kdeXdgForeignPendingHandleProperty, handle);
|
||||
window->installEventFilter(this);
|
||||
#else
|
||||
connect(waylandWindow, &QNativeInterface::Private::QWaylandWindow::surfaceRoleCreated, window, [window, handle] {
|
||||
doSetMainWindow(window, handle);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowSystem::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0)
|
||||
if (event->type() == QEvent::Expose) {
|
||||
auto *window = static_cast<QWindow *>(watched);
|
||||
if (window->isExposed()) {
|
||||
const QString handle = window->property(c_kdeXdgForeignPendingHandleProperty).toString();
|
||||
if (!handle.isEmpty()) {
|
||||
doSetMainWindow(window, handle);
|
||||
window->setProperty(c_kdeXdgForeignPendingHandleProperty, QVariant());
|
||||
}
|
||||
|
||||
window->removeEventFilter(this);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return QObject::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void WindowSystem::doSetMainWindow(QWindow *window, const QString &handle)
|
||||
{
|
||||
Q_ASSERT(window);
|
||||
Q_ASSERT(!handle.isEmpty());
|
||||
|
||||
auto waylandWindow = window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
|
||||
if (!waylandWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &importer = WaylandXdgForeignImporterV2::self();
|
||||
if (!importer.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(!waylandWindow->property(c_kdeXdgForeignImportedProperty).isValid());
|
||||
|
||||
WaylandXdgForeignImportedV2 *imported = importer.importToplevel(handle);
|
||||
imported->set_parent_of(surfaceForWindow(window)); // foreign parent.
|
||||
imported->setParent(waylandWindow); // memory owner.
|
||||
|
||||
waylandWindow->setProperty(c_kdeXdgForeignImportedProperty, QVariant::fromValue(imported));
|
||||
connect(imported, &QObject::destroyed, waylandWindow, [waylandWindow] {
|
||||
waylandWindow->setProperty(c_kdeXdgForeignImportedProperty, QVariant());
|
||||
});
|
||||
}
|
||||
|
||||
#include "moc_windowsystem.cpp"
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#ifndef WINDOWSYSTEM_H
|
||||
#define WINDOWSYSTEM_H
|
||||
|
||||
#include "kwindowsystem_p.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class WindowManagement;
|
||||
|
||||
class WindowSystem : public QObject, public KWindowSystemPrivateV2
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
WindowSystem();
|
||||
~WindowSystem() override;
|
||||
void activateWindow(QWindow *win, long time) override;
|
||||
void requestToken(QWindow *win, uint32_t serial, const QString &app_id) override;
|
||||
quint32 lastInputSerial(QWindow *window) override;
|
||||
void setCurrentToken(const QString &token) override;
|
||||
bool showingDesktop() override;
|
||||
void setShowingDesktop(bool showing) override;
|
||||
void exportWindow(QWindow *window) override;
|
||||
void unexportWindow(QWindow *window) override;
|
||||
void setMainWindow(QWindow *window, const QString &handle) override;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
private:
|
||||
static void doSetMainWindow(QWindow *window, const QString &handle);
|
||||
QString m_lastToken;
|
||||
WindowManagement *m_windowManagement;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
add_library(KF6WindowSystemX11Plugin MODULE)
|
||||
|
||||
target_sources(KF6WindowSystemX11Plugin PRIVATE
|
||||
kwindoweffects.cpp
|
||||
kwindowshadow.cpp
|
||||
kwindowsystem.cpp
|
||||
kxutils.cpp
|
||||
plugin.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(KF6WindowSystemX11Plugin
|
||||
PRIVATE
|
||||
KF6WindowSystem
|
||||
XCB::XCB
|
||||
XCB::RES
|
||||
${X11_LIBRARIES}
|
||||
${X11_Xfixes_LIB}
|
||||
Qt6::GuiPrivate
|
||||
)
|
||||
|
||||
ecm_generate_headers(KWindowSystemX11_HEADERS
|
||||
HEADER_NAMES
|
||||
KSelectionOwner
|
||||
KSelectionWatcher
|
||||
KXMessages
|
||||
NETWM # does not match the classnames in that file...
|
||||
|
||||
REQUIRED_HEADERS
|
||||
KWindowSystemX11_HEADERS
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
${KWindowSystemX11_HEADERS}
|
||||
fixx11h.h
|
||||
DESTINATION
|
||||
${KDE_INSTALL_INCLUDEDIR_KF}/KWindowSystem
|
||||
COMPONENT
|
||||
Devel
|
||||
)
|
||||
|
||||
set_target_properties(
|
||||
KF6WindowSystemX11Plugin
|
||||
PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/kf6/kwindowsystem"
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
KF6WindowSystemX11Plugin
|
||||
DESTINATION
|
||||
${KDE_INSTALL_PLUGINDIR}/kf6/kwindowsystem/
|
||||
)
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Thomas Lübking <thomas.luebking@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#if (!defined ATOMS_H) || (defined ENUM_CREATE_CHAR_ARRAY)
|
||||
|
||||
#undef ENUM_BEGIN
|
||||
#undef ENUM
|
||||
#undef ENUM_END
|
||||
#undef ENUM_COUNT
|
||||
|
||||
// the following macros are set in a way so that
|
||||
// the code below will either construct an enum for "<typ>"
|
||||
// or a const *char array "<typ>Strings" containing all enum
|
||||
// symbols as strings, depending on whether ENUM_CREATE_CHAR_ARRAY is
|
||||
// defined
|
||||
// The enum gets one extra item "<typ>Count", describing also the
|
||||
// length of the array
|
||||
|
||||
// The header is safe for re-inclusion unless you define ENUM_CREATE_CHAR_ARRAY
|
||||
// which is therefore undefined after usage
|
||||
|
||||
// => You *must* "#define ENUM_CREATE_CHAR_ARRAY 1" *every* time you want to create
|
||||
// a string array!
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef ENUM_CREATE_CHAR_ARRAY
|
||||
#define ATOMS_H
|
||||
#define ENUM_BEGIN(typ) enum typ {
|
||||
#define ENUM(nam) nam
|
||||
#define ENUM_COUNT(typ) , typ##Count
|
||||
#else
|
||||
#define ENUM_BEGIN(typ) const char * typ##Strings [] = {
|
||||
#define ENUM(nam) #nam
|
||||
#define ENUM_COUNT(typ)
|
||||
#undef ENUM_CREATE_CHAR_ARRAY
|
||||
#endif
|
||||
|
||||
#define ENUM_END(typ) };
|
||||
|
||||
ENUM_BEGIN(KwsAtom)
|
||||
ENUM(UTF8_STRING),
|
||||
|
||||
// root window properties
|
||||
ENUM(_NET_SUPPORTED),
|
||||
ENUM(_NET_SUPPORTING_WM_CHECK),
|
||||
ENUM(_NET_CLIENT_LIST),
|
||||
ENUM(_NET_CLIENT_LIST_STACKING),
|
||||
ENUM(_NET_NUMBER_OF_DESKTOPS),
|
||||
ENUM(_NET_DESKTOP_GEOMETRY),
|
||||
ENUM(_NET_DESKTOP_VIEWPORT),
|
||||
ENUM(_NET_CURRENT_DESKTOP),
|
||||
ENUM(_NET_DESKTOP_NAMES),
|
||||
ENUM(_NET_ACTIVE_WINDOW),
|
||||
ENUM(_NET_WORKAREA),
|
||||
ENUM(_NET_VIRTUAL_ROOTS),
|
||||
ENUM(_NET_DESKTOP_LAYOUT),
|
||||
ENUM(_NET_SHOWING_DESKTOP),
|
||||
|
||||
// root window messages
|
||||
ENUM(_NET_CLOSE_WINDOW),
|
||||
ENUM(_NET_RESTACK_WINDOW),
|
||||
ENUM(_NET_WM_MOVERESIZE),
|
||||
ENUM(_NET_MOVERESIZE_WINDOW),
|
||||
|
||||
// application window properties
|
||||
ENUM(_NET_WM_NAME),
|
||||
ENUM(_NET_WM_VISIBLE_NAME),
|
||||
ENUM(_NET_WM_ICON_NAME),
|
||||
ENUM(_NET_WM_VISIBLE_ICON_NAME),
|
||||
ENUM(_NET_WM_DESKTOP),
|
||||
ENUM(_NET_WM_WINDOW_TYPE),
|
||||
ENUM(_NET_WM_STATE),
|
||||
ENUM(_NET_WM_STRUT),
|
||||
ENUM(_NET_WM_STRUT_PARTIAL),
|
||||
ENUM(_NET_WM_ICON_GEOMETRY),
|
||||
ENUM(_NET_WM_ICON),
|
||||
ENUM(_NET_WM_PID),
|
||||
ENUM(_NET_WM_USER_TIME),
|
||||
ENUM(_NET_WM_HANDLED_ICONS),
|
||||
ENUM(_NET_STARTUP_ID),
|
||||
ENUM(_NET_WM_ALLOWED_ACTIONS),
|
||||
ENUM(WM_WINDOW_ROLE),
|
||||
ENUM(_NET_FRAME_EXTENTS),
|
||||
ENUM(_NET_WM_WINDOW_OPACITY),
|
||||
ENUM(_NET_WM_FULLSCREEN_MONITORS),
|
||||
ENUM(_NET_WM_OPAQUE_REGION),
|
||||
ENUM(_KDE_NET_WM_DESKTOP_FILE),
|
||||
// used to determine whether application window is managed or not
|
||||
ENUM(WM_STATE),
|
||||
|
||||
// application window types
|
||||
ENUM(_NET_WM_WINDOW_TYPE_NORMAL),
|
||||
ENUM(_NET_WM_WINDOW_TYPE_DESKTOP),
|
||||
ENUM(_NET_WM_WINDOW_TYPE_DOCK),
|
||||
ENUM(_NET_WM_WINDOW_TYPE_TOOLBAR),
|
||||
ENUM(_NET_WM_WINDOW_TYPE_MENU),
|
||||
ENUM(_NET_WM_WINDOW_TYPE_DIALOG),
|
||||
ENUM(_NET_WM_WINDOW_TYPE_UTILITY),
|
||||
ENUM(_NET_WM_WINDOW_TYPE_SPLASH),
|
||||
ENUM(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU),
|
||||
ENUM(_NET_WM_WINDOW_TYPE_POPUP_MENU),
|
||||
ENUM(_NET_WM_WINDOW_TYPE_TOOLTIP),
|
||||
ENUM(_NET_WM_WINDOW_TYPE_NOTIFICATION),
|
||||
ENUM(_NET_WM_WINDOW_TYPE_COMBO),
|
||||
ENUM(_NET_WM_WINDOW_TYPE_DND),
|
||||
|
||||
// application window state
|
||||
ENUM(_NET_WM_STATE_MODAL),
|
||||
ENUM(_NET_WM_STATE_STICKY),
|
||||
ENUM(_NET_WM_STATE_MAXIMIZED_VERT),
|
||||
ENUM(_NET_WM_STATE_MAXIMIZED_HORZ),
|
||||
ENUM(_NET_WM_STATE_SHADED),
|
||||
ENUM(_NET_WM_STATE_SKIP_TASKBAR),
|
||||
ENUM(_NET_WM_STATE_SKIP_PAGER),
|
||||
ENUM(_NET_WM_STATE_HIDDEN),
|
||||
ENUM(_NET_WM_STATE_FULLSCREEN),
|
||||
ENUM(_NET_WM_STATE_ABOVE),
|
||||
ENUM(_NET_WM_STATE_BELOW),
|
||||
ENUM(_NET_WM_STATE_DEMANDS_ATTENTION),
|
||||
ENUM(_NET_WM_STATE_FOCUSED),
|
||||
// KDE-specific atom
|
||||
ENUM(_KDE_NET_WM_STATE_SKIP_SWITCHER),
|
||||
|
||||
// allowed actions
|
||||
ENUM(_NET_WM_ACTION_MOVE),
|
||||
ENUM(_NET_WM_ACTION_RESIZE),
|
||||
ENUM(_NET_WM_ACTION_MINIMIZE),
|
||||
ENUM(_NET_WM_ACTION_SHADE),
|
||||
ENUM(_NET_WM_ACTION_STICK),
|
||||
ENUM(_NET_WM_ACTION_MAXIMIZE_VERT),
|
||||
ENUM(_NET_WM_ACTION_MAXIMIZE_HORZ),
|
||||
ENUM(_NET_WM_ACTION_FULLSCREEN),
|
||||
ENUM(_NET_WM_ACTION_CHANGE_DESKTOP),
|
||||
ENUM(_NET_WM_ACTION_CLOSE),
|
||||
|
||||
// KDE extensions
|
||||
ENUM(_KDE_NET_WM_FRAME_STRUT),
|
||||
ENUM(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE),
|
||||
ENUM(_KDE_NET_WM_WINDOW_TYPE_TOPMENU),
|
||||
ENUM(_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY),
|
||||
ENUM(_KDE_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION),
|
||||
ENUM(_KDE_NET_WM_WINDOW_TYPE_APPLET_POPUP),
|
||||
ENUM(_KDE_NET_WM_TEMPORARY_RULES),
|
||||
ENUM(_NET_WM_FRAME_OVERLAP),
|
||||
ENUM(_KDE_NET_WM_APPMENU_SERVICE_NAME),
|
||||
ENUM(_KDE_NET_WM_APPMENU_OBJECT_PATH),
|
||||
|
||||
// deprecated and naming convention violation
|
||||
ENUM(_NET_WM_STATE_STAYS_ON_TOP),
|
||||
|
||||
// GTK extensions
|
||||
ENUM(_GTK_FRAME_EXTENTS),
|
||||
ENUM(_GTK_APPLICATION_ID),
|
||||
ENUM(_GTK_SHOW_WINDOW_MENU),
|
||||
|
||||
// application protocols
|
||||
ENUM(WM_PROTOCOLS),
|
||||
ENUM(WM_TAKE_FOCUS),
|
||||
ENUM(WM_DELETE_WINDOW),
|
||||
ENUM(_NET_WM_PING),
|
||||
ENUM(_NET_WM_SYNC_REQUEST),
|
||||
ENUM(_NET_WM_CONTEXT_HELP),
|
||||
|
||||
// ability flags
|
||||
ENUM(_NET_WM_FULL_PLACEMENT),
|
||||
ENUM(_NET_WM_BYPASS_COMPOSITOR),
|
||||
ENUM(_KDE_NET_WM_ACTIVITIES),
|
||||
ENUM(_KDE_NET_WM_BLOCK_COMPOSITING),
|
||||
ENUM(_KDE_NET_WM_SHADOW)
|
||||
ENUM_COUNT(KwsAtom)
|
||||
ENUM_END(KwsAtom)
|
||||
|
||||
#endif // ATOMS_H
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
//#ifdef don't do this, this file is supposed to be included
|
||||
//#define multiple times
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
/* Usage:
|
||||
|
||||
If you get compile errors caused by X11 includes (the line
|
||||
where first error appears contains word like None, Unsorted,
|
||||
Below, etc.), put #include <fixx11h.h> in the .cpp file
|
||||
(not .h file!) between the place where X11 headers are
|
||||
included and the place where the file with compile
|
||||
error is included (or the place where the compile error
|
||||
in the .cpp file occurs).
|
||||
|
||||
This file remaps X11 #defines to const variables or
|
||||
inline functions. The side effect may be that these
|
||||
symbols may now refer to different variables
|
||||
(e.g. if X11 #defined NoButton, after this file
|
||||
is included NoButton would no longer be X11's
|
||||
NoButton, but Qt::NoButton instead). At this time,
|
||||
there's no conflict known that could cause problems.
|
||||
|
||||
The original X11 symbols are still accessible
|
||||
(e.g. for None) as X::None, XNone, and also still
|
||||
None, unless name lookup finds different None
|
||||
first (in the current class, etc.)
|
||||
|
||||
Use 'Unsorted', 'Bool' and 'index' as templates.
|
||||
|
||||
*/
|
||||
|
||||
namespace X
|
||||
{
|
||||
// template --->
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Unsorted
|
||||
#ifndef FIXX11H_Unsorted
|
||||
#define FIXX11H_Unsorted
|
||||
const int XUnsorted = Unsorted;
|
||||
#undef Unsorted
|
||||
const int Unsorted = XUnsorted;
|
||||
#endif
|
||||
#undef Unsorted
|
||||
#endif
|
||||
// template <---
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef None
|
||||
#ifndef FIXX11H_None
|
||||
#define FIXX11H_None
|
||||
const XID XNone = None;
|
||||
#undef None
|
||||
const XID None = XNone;
|
||||
#endif
|
||||
#undef None
|
||||
#endif
|
||||
|
||||
// template --->
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Bool
|
||||
#ifndef FIXX11H_Bool
|
||||
#define FIXX11H_Bool
|
||||
#ifdef _XTYPEDEF_BOOL /* Xdefs.h has typedef'ed Bool already */
|
||||
#undef Bool
|
||||
#else
|
||||
typedef Bool XBool;
|
||||
#undef Bool
|
||||
typedef XBool Bool;
|
||||
#endif
|
||||
#endif
|
||||
#undef Bool
|
||||
#define _XTYPEDEF_BOOL
|
||||
#endif
|
||||
// template <---
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef KeyPress
|
||||
#ifndef FIXX11H_KeyPress
|
||||
#define FIXX11H_KeyPress
|
||||
const int XKeyPress = KeyPress;
|
||||
#undef KeyPress
|
||||
const int KeyPress = XKeyPress;
|
||||
#endif
|
||||
#undef KeyPress
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef KeyRelease
|
||||
#ifndef FIXX11H_KeyRelease
|
||||
#define FIXX11H_KeyRelease
|
||||
const int XKeyRelease = KeyRelease;
|
||||
#undef KeyRelease
|
||||
const int KeyRelease = XKeyRelease;
|
||||
#endif
|
||||
#undef KeyRelease
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Above
|
||||
#ifndef FIXX11H_Above
|
||||
#define FIXX11H_Above
|
||||
const int XAbove = Above;
|
||||
#undef Above
|
||||
const int Above = XAbove;
|
||||
#endif
|
||||
#undef Above
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Below
|
||||
#ifndef FIXX11H_Below
|
||||
#define FIXX11H_Below
|
||||
const int XBelow = Below;
|
||||
#undef Below
|
||||
const int Below = XBelow;
|
||||
#endif
|
||||
#undef Below
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef FocusIn
|
||||
#ifndef FIXX11H_FocusIn
|
||||
#define FIXX11H_FocusIn
|
||||
const int XFocusIn = FocusIn;
|
||||
#undef FocusIn
|
||||
const int FocusIn = XFocusIn;
|
||||
#endif
|
||||
#undef FocusIn
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef FocusOut
|
||||
#ifndef FIXX11H_FocusOut
|
||||
#define FIXX11H_FocusOut
|
||||
const int XFocusOut = FocusOut;
|
||||
#undef FocusOut
|
||||
const int FocusOut = XFocusOut;
|
||||
#endif
|
||||
#undef FocusOut
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Always
|
||||
#ifndef FIXX11H_Always
|
||||
#define FIXX11H_Always
|
||||
const int XAlways = Always;
|
||||
#undef Always
|
||||
const int Always = XAlways;
|
||||
#endif
|
||||
#undef Always
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Expose
|
||||
#ifndef FIXX11H_Expose
|
||||
#define FIXX11H_Expose
|
||||
const int XExpose = Expose;
|
||||
#undef Expose
|
||||
const int Expose = XExpose;
|
||||
#endif
|
||||
#undef Expose
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Success
|
||||
#ifndef FIXX11H_Success
|
||||
#define FIXX11H_Success
|
||||
const int XSuccess = Success;
|
||||
#undef Success
|
||||
const int Success = XSuccess;
|
||||
#endif
|
||||
#undef Success
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef GrayScale
|
||||
#ifndef FIXX11H_GrayScale
|
||||
#define FIXX11H_GrayScale
|
||||
const int XGrayScale = GrayScale;
|
||||
#undef GrayScale
|
||||
const int GrayScale = XGrayScale;
|
||||
#endif
|
||||
#undef GrayScale
|
||||
#endif
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef Status
|
||||
#ifndef FIXX11H_Status
|
||||
#define FIXX11H_Status
|
||||
typedef Status XStatus;
|
||||
#undef Status
|
||||
typedef XStatus Status;
|
||||
#endif
|
||||
#undef Status
|
||||
#endif
|
||||
|
||||
// template --->
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef CursorShape
|
||||
#ifndef FIXX11H_CursorShape
|
||||
#define FIXX11H_CursorShape
|
||||
const int XCursorShape = CursorShape;
|
||||
#undef CursorShape
|
||||
const int CursorShape = XCursorShape;
|
||||
#endif
|
||||
#undef CursorShape
|
||||
#endif
|
||||
// template <---
|
||||
|
||||
// template --->
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef FontChange
|
||||
#ifndef FIXX11H_FontChange
|
||||
#define FIXX11H_FontChange
|
||||
const int XFontChange = FontChange;
|
||||
#undef FontChange
|
||||
const int FontChange = XFontChange;
|
||||
#endif
|
||||
#undef FontChange
|
||||
#endif
|
||||
// template <---
|
||||
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef NormalState
|
||||
#ifndef FIXX11H_NormalState
|
||||
#define FIXX11H_NormalState
|
||||
const int XNormalState = NormalState;
|
||||
#undef NormalState
|
||||
const int NormalState = XNormalState;
|
||||
#endif
|
||||
#undef NormalState
|
||||
#endif
|
||||
|
||||
// template --->
|
||||
// Affects: Should be without side effects.
|
||||
#ifdef index
|
||||
#ifndef FIXX11H_index
|
||||
#define FIXX11H_index
|
||||
inline const char *Xindex(const char *s, int c)
|
||||
{
|
||||
return index(s, c);
|
||||
}
|
||||
#undef index
|
||||
inline const char *index(const char *s, int c)
|
||||
{
|
||||
return Xindex(s, c);
|
||||
}
|
||||
#endif
|
||||
#undef index
|
||||
#endif
|
||||
// template <---
|
||||
|
||||
#ifdef rindex
|
||||
// Affects: Should be without side effects.
|
||||
#ifndef FIXX11H_rindex
|
||||
#define FIXX11H_rindex
|
||||
inline const char *Xrindex(const char *s, int c)
|
||||
{
|
||||
return rindex(s, c);
|
||||
}
|
||||
#undef rindex
|
||||
inline const char *rindex(const char *s, int c)
|
||||
{
|
||||
return Xrindex(s, c);
|
||||
}
|
||||
#endif
|
||||
#undef rindex
|
||||
#endif
|
||||
}
|
||||
|
||||
using namespace X;
|
||||
@@ -0,0 +1,615 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "kselectionowner.h"
|
||||
|
||||
#include "kwindowsystem.h"
|
||||
#include "kxcbevent_p.h"
|
||||
#include <config-kwindowsystem.h>
|
||||
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QBasicTimer>
|
||||
#include <QDebug>
|
||||
#include <QGuiApplication>
|
||||
#include <QTimerEvent>
|
||||
|
||||
#include <private/qtx11extras_p.h>
|
||||
|
||||
static xcb_window_t get_selection_owner(xcb_connection_t *c, xcb_atom_t selection)
|
||||
{
|
||||
xcb_window_t owner = XCB_NONE;
|
||||
xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(c, xcb_get_selection_owner(c, selection), nullptr);
|
||||
|
||||
if (reply) {
|
||||
owner = reply->owner;
|
||||
free(reply);
|
||||
}
|
||||
|
||||
return owner;
|
||||
}
|
||||
|
||||
static xcb_atom_t intern_atom(xcb_connection_t *c, const char *name)
|
||||
{
|
||||
xcb_atom_t atom = XCB_NONE;
|
||||
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, xcb_intern_atom(c, false, strlen(name), name), nullptr);
|
||||
|
||||
if (reply) {
|
||||
atom = reply->atom;
|
||||
free(reply);
|
||||
}
|
||||
|
||||
return atom;
|
||||
}
|
||||
|
||||
class Q_DECL_HIDDEN KSelectionOwner::Private : public QAbstractNativeEventFilter
|
||||
{
|
||||
public:
|
||||
enum State { Idle, WaitingForTimestamp, WaitingForPreviousOwner };
|
||||
|
||||
Private(KSelectionOwner *owner_P, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
|
||||
: state(Idle)
|
||||
, selection(selection_P)
|
||||
, connection(c)
|
||||
, root(root)
|
||||
, window(XCB_NONE)
|
||||
, prev_owner(XCB_NONE)
|
||||
, timestamp(XCB_CURRENT_TIME)
|
||||
, extra1(0)
|
||||
, extra2(0)
|
||||
, force_kill(false)
|
||||
, owner(owner_P)
|
||||
{
|
||||
QCoreApplication::instance()->installNativeEventFilter(this);
|
||||
}
|
||||
|
||||
void claimSucceeded();
|
||||
void gotTimestamp();
|
||||
void timeout();
|
||||
|
||||
State state;
|
||||
const xcb_atom_t selection;
|
||||
xcb_connection_t *connection;
|
||||
xcb_window_t root;
|
||||
xcb_window_t window;
|
||||
xcb_window_t prev_owner;
|
||||
xcb_timestamp_t timestamp;
|
||||
uint32_t extra1, extra2;
|
||||
QBasicTimer timer;
|
||||
bool force_kill;
|
||||
static xcb_atom_t manager_atom;
|
||||
static xcb_atom_t xa_multiple;
|
||||
static xcb_atom_t xa_targets;
|
||||
static xcb_atom_t xa_timestamp;
|
||||
|
||||
static Private *create(KSelectionOwner *owner, xcb_atom_t selection_P, int screen_P);
|
||||
static Private *create(KSelectionOwner *owner, const char *selection_P, int screen_P);
|
||||
static Private *create(KSelectionOwner *owner, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root);
|
||||
static Private *create(KSelectionOwner *owner, const char *selection_P, xcb_connection_t *c, xcb_window_t root);
|
||||
|
||||
protected:
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override
|
||||
{
|
||||
if (eventType != "xcb_generic_event_t") {
|
||||
return false;
|
||||
}
|
||||
return owner->filterEvent(message);
|
||||
}
|
||||
|
||||
private:
|
||||
KSelectionOwner *owner;
|
||||
};
|
||||
|
||||
KSelectionOwner::Private *KSelectionOwner::Private::create(KSelectionOwner *owner, xcb_atom_t selection_P, int screen_P)
|
||||
{
|
||||
if (KWindowSystem::isPlatformX11()) {
|
||||
return create(owner, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
|
||||
}
|
||||
qWarning() << "Trying to use KSelectionOwner on a non-X11 platform! This is an application bug.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KSelectionOwner::Private *KSelectionOwner::Private::create(KSelectionOwner *owner, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
|
||||
{
|
||||
return new Private(owner, selection_P, c, root);
|
||||
}
|
||||
|
||||
KSelectionOwner::Private *KSelectionOwner::Private::create(KSelectionOwner *owner, const char *selection_P, int screen_P)
|
||||
{
|
||||
if (KWindowSystem::isPlatformX11()) {
|
||||
return create(owner, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
|
||||
}
|
||||
qWarning() << "Trying to use KSelectionOwner on a non-X11 platform! This is an application bug.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KSelectionOwner::Private *KSelectionOwner::Private::create(KSelectionOwner *owner, const char *selection_P, xcb_connection_t *c, xcb_window_t root)
|
||||
{
|
||||
return new Private(owner, intern_atom(c, selection_P), c, root);
|
||||
}
|
||||
|
||||
KSelectionOwner::KSelectionOwner(xcb_atom_t selection_P, int screen_P, QObject *parent_P)
|
||||
: QObject(parent_P)
|
||||
, d(Private::create(this, selection_P, screen_P))
|
||||
{
|
||||
}
|
||||
|
||||
KSelectionOwner::KSelectionOwner(const char *selection_P, int screen_P, QObject *parent_P)
|
||||
: QObject(parent_P)
|
||||
, d(Private::create(this, selection_P, screen_P))
|
||||
{
|
||||
}
|
||||
|
||||
KSelectionOwner::KSelectionOwner(xcb_atom_t selection, xcb_connection_t *c, xcb_window_t root, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(Private::create(this, selection, c, root))
|
||||
{
|
||||
}
|
||||
|
||||
KSelectionOwner::KSelectionOwner(const char *selection, xcb_connection_t *c, xcb_window_t root, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(Private::create(this, selection, c, root))
|
||||
{
|
||||
}
|
||||
|
||||
KSelectionOwner::~KSelectionOwner()
|
||||
{
|
||||
if (d) {
|
||||
release();
|
||||
if (d->window != XCB_WINDOW_NONE) {
|
||||
xcb_destroy_window(d->connection, d->window); // also makes the selection not owned
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
|
||||
void KSelectionOwner::Private::claimSucceeded()
|
||||
{
|
||||
state = Idle;
|
||||
|
||||
KXcbEvent<xcb_client_message_event_t> ev;
|
||||
ev.response_type = XCB_CLIENT_MESSAGE;
|
||||
ev.format = 32;
|
||||
ev.window = root;
|
||||
ev.type = Private::manager_atom;
|
||||
ev.data.data32[0] = timestamp;
|
||||
ev.data.data32[1] = selection;
|
||||
ev.data.data32[2] = window;
|
||||
ev.data.data32[3] = extra1;
|
||||
ev.data.data32[4] = extra2;
|
||||
|
||||
xcb_send_event(connection, false, root, XCB_EVENT_MASK_STRUCTURE_NOTIFY, ev.buffer());
|
||||
|
||||
// qDebug() << "Claimed selection";
|
||||
|
||||
Q_EMIT owner->claimedOwnership();
|
||||
}
|
||||
|
||||
void KSelectionOwner::Private::gotTimestamp()
|
||||
{
|
||||
Q_ASSERT(state == WaitingForTimestamp);
|
||||
|
||||
state = Idle;
|
||||
|
||||
xcb_connection_t *c = connection;
|
||||
|
||||
// Set the selection owner and immediately verify that the claim was successful
|
||||
xcb_set_selection_owner(c, window, selection, timestamp);
|
||||
xcb_window_t new_owner = get_selection_owner(c, selection);
|
||||
|
||||
if (new_owner != window) {
|
||||
// qDebug() << "Failed to claim selection : " << new_owner;
|
||||
xcb_destroy_window(c, window);
|
||||
timestamp = XCB_CURRENT_TIME;
|
||||
window = XCB_NONE;
|
||||
|
||||
Q_EMIT owner->failedToClaimOwnership();
|
||||
return;
|
||||
}
|
||||
|
||||
if (prev_owner != XCB_NONE && force_kill) {
|
||||
// qDebug() << "Waiting for previous owner to disown";
|
||||
timer.start(1000, owner);
|
||||
state = WaitingForPreviousOwner;
|
||||
|
||||
// Note: We've already selected for structure notify events
|
||||
// on the previous owner window
|
||||
} else {
|
||||
// If there was no previous owner, we're done
|
||||
claimSucceeded();
|
||||
}
|
||||
}
|
||||
|
||||
void KSelectionOwner::Private::timeout()
|
||||
{
|
||||
Q_ASSERT(state == WaitingForPreviousOwner);
|
||||
|
||||
state = Idle;
|
||||
|
||||
if (force_kill) {
|
||||
// qDebug() << "Killing previous owner";
|
||||
xcb_connection_t *c = connection;
|
||||
|
||||
// Ignore any errors from the kill request
|
||||
xcb_generic_error_t *err = xcb_request_check(c, xcb_kill_client_checked(c, prev_owner));
|
||||
free(err);
|
||||
|
||||
claimSucceeded();
|
||||
} else {
|
||||
Q_EMIT owner->failedToClaimOwnership();
|
||||
}
|
||||
}
|
||||
|
||||
void KSelectionOwner::claim(bool force_P, bool force_kill_P)
|
||||
{
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
Q_ASSERT(d->state == Private::Idle);
|
||||
|
||||
if (Private::manager_atom == XCB_NONE) {
|
||||
getAtoms();
|
||||
}
|
||||
|
||||
if (d->timestamp != XCB_CURRENT_TIME) {
|
||||
release();
|
||||
}
|
||||
|
||||
xcb_connection_t *c = d->connection;
|
||||
d->prev_owner = get_selection_owner(c, d->selection);
|
||||
|
||||
if (d->prev_owner != XCB_NONE) {
|
||||
if (!force_P) {
|
||||
// qDebug() << "Selection already owned, failing";
|
||||
Q_EMIT failedToClaimOwnership();
|
||||
return;
|
||||
}
|
||||
|
||||
// Select structure notify events so get an event when the previous owner
|
||||
// destroys the window
|
||||
uint32_t mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
|
||||
xcb_change_window_attributes(c, d->prev_owner, XCB_CW_EVENT_MASK, &mask);
|
||||
}
|
||||
|
||||
uint32_t values[] = {true, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
|
||||
|
||||
d->window = xcb_generate_id(c);
|
||||
xcb_create_window(c,
|
||||
XCB_COPY_FROM_PARENT,
|
||||
d->window,
|
||||
d->root,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
XCB_WINDOW_CLASS_INPUT_ONLY,
|
||||
XCB_COPY_FROM_PARENT,
|
||||
XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
|
||||
values);
|
||||
|
||||
// Trigger a property change event so we get a timestamp
|
||||
xcb_atom_t tmp = XCB_ATOM_ATOM;
|
||||
xcb_change_property(c, XCB_PROP_MODE_REPLACE, d->window, XCB_ATOM_ATOM, XCB_ATOM_ATOM, 32, 1, (const void *)&tmp);
|
||||
|
||||
// Now we have to return to the event loop and wait for the property change event
|
||||
d->force_kill = force_kill_P;
|
||||
d->state = Private::WaitingForTimestamp;
|
||||
}
|
||||
|
||||
// destroy resource first
|
||||
void KSelectionOwner::release()
|
||||
{
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
if (d->timestamp == XCB_CURRENT_TIME) {
|
||||
return;
|
||||
}
|
||||
|
||||
xcb_destroy_window(d->connection, d->window); // also makes the selection not owned
|
||||
d->window = XCB_NONE;
|
||||
|
||||
// qDebug() << "Releasing selection";
|
||||
|
||||
d->timestamp = XCB_CURRENT_TIME;
|
||||
}
|
||||
|
||||
xcb_window_t KSelectionOwner::ownerWindow() const
|
||||
{
|
||||
if (!d) {
|
||||
return XCB_WINDOW_NONE;
|
||||
}
|
||||
if (d->timestamp == XCB_CURRENT_TIME) {
|
||||
return XCB_NONE;
|
||||
}
|
||||
|
||||
return d->window;
|
||||
}
|
||||
|
||||
void KSelectionOwner::setData(uint32_t extra1_P, uint32_t extra2_P)
|
||||
{
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
d->extra1 = extra1_P;
|
||||
d->extra2 = extra2_P;
|
||||
}
|
||||
|
||||
bool KSelectionOwner::filterEvent(void *ev_P)
|
||||
{
|
||||
if (!d) {
|
||||
return false;
|
||||
}
|
||||
xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t *>(ev_P);
|
||||
const uint response_type = event->response_type & ~0x80;
|
||||
|
||||
#if 0
|
||||
// There's no generic way to get the window for an event in xcb, it depends on the type of event
|
||||
// This handleMessage virtual doesn't seem used anyway.
|
||||
if (d->timestamp != CurrentTime && ev_P->xany.window == d->window) {
|
||||
if (handleMessage(ev_P)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
switch (response_type) {
|
||||
case XCB_SELECTION_CLEAR: {
|
||||
xcb_selection_clear_event_t *ev = reinterpret_cast<xcb_selection_clear_event_t *>(event);
|
||||
if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d->timestamp = XCB_CURRENT_TIME;
|
||||
// qDebug() << "Lost selection";
|
||||
|
||||
xcb_window_t window = d->window;
|
||||
Q_EMIT lostOwnership();
|
||||
|
||||
// Unset the event mask before we destroy the window so we don't get a destroy event
|
||||
uint32_t event_mask = XCB_NONE;
|
||||
xcb_change_window_attributes(d->connection, window, XCB_CW_EVENT_MASK, &event_mask);
|
||||
xcb_destroy_window(d->connection, window);
|
||||
return true;
|
||||
}
|
||||
case XCB_DESTROY_NOTIFY: {
|
||||
xcb_destroy_notify_event_t *ev = reinterpret_cast<xcb_destroy_notify_event_t *>(event);
|
||||
if (ev->window == d->prev_owner) {
|
||||
if (d->state == Private::WaitingForPreviousOwner) {
|
||||
d->timer.stop();
|
||||
d->claimSucceeded();
|
||||
return true;
|
||||
}
|
||||
// It is possible for the previous owner to be destroyed
|
||||
// while we're waiting for the timestamp
|
||||
d->prev_owner = XCB_NONE;
|
||||
}
|
||||
|
||||
if (d->timestamp == XCB_CURRENT_TIME || ev->window != d->window) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d->timestamp = XCB_CURRENT_TIME;
|
||||
// qDebug() << "Lost selection (destroyed)";
|
||||
Q_EMIT lostOwnership();
|
||||
return true;
|
||||
}
|
||||
case XCB_SELECTION_NOTIFY: {
|
||||
xcb_selection_notify_event_t *ev = reinterpret_cast<xcb_selection_notify_event_t *>(event);
|
||||
if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ignore?
|
||||
return false;
|
||||
}
|
||||
case XCB_SELECTION_REQUEST:
|
||||
filter_selection_request(event);
|
||||
return false;
|
||||
case XCB_PROPERTY_NOTIFY: {
|
||||
xcb_property_notify_event_t *ev = reinterpret_cast<xcb_property_notify_event_t *>(event);
|
||||
if (ev->window == d->window && d->state == Private::WaitingForTimestamp) {
|
||||
d->timestamp = ev->time;
|
||||
d->gotTimestamp();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void KSelectionOwner::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
if (!d) {
|
||||
QObject::timerEvent(event);
|
||||
return;
|
||||
}
|
||||
if (event->timerId() == d->timer.timerId()) {
|
||||
d->timer.stop();
|
||||
d->timeout();
|
||||
return;
|
||||
}
|
||||
|
||||
QObject::timerEvent(event);
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool KSelectionOwner::handleMessage(XEvent *)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void KSelectionOwner::filter_selection_request(void *event)
|
||||
{
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
xcb_selection_request_event_t *ev = reinterpret_cast<xcb_selection_request_event_t *>(event);
|
||||
|
||||
if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev->time != XCB_CURRENT_TIME && ev->time - d->timestamp > 1U << 31) {
|
||||
return; // too old or too new request
|
||||
}
|
||||
|
||||
// qDebug() << "Got selection request";
|
||||
|
||||
xcb_connection_t *c = d->connection;
|
||||
bool handled = false;
|
||||
|
||||
if (ev->target == Private::xa_multiple) {
|
||||
if (ev->property != XCB_NONE) {
|
||||
const int MAX_ATOMS = 100;
|
||||
|
||||
xcb_get_property_cookie_t cookie = xcb_get_property(c, false, ev->requestor, ev->property, XCB_GET_PROPERTY_TYPE_ANY, 0, MAX_ATOMS);
|
||||
xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, nullptr);
|
||||
|
||||
if (reply && reply->format == 32 && reply->value_len % 2 == 0) {
|
||||
xcb_atom_t *atoms = reinterpret_cast<xcb_atom_t *>(xcb_get_property_value(reply));
|
||||
bool handled_array[MAX_ATOMS];
|
||||
|
||||
for (uint i = 0; i < reply->value_len / 2; i++) {
|
||||
handled_array[i] = handle_selection(atoms[i * 2], atoms[i * 2 + 1], ev->requestor);
|
||||
}
|
||||
|
||||
bool all_handled = true;
|
||||
for (uint i = 0; i < reply->value_len / 2; i++) {
|
||||
if (!handled_array[i]) {
|
||||
all_handled = false;
|
||||
atoms[i * 2 + 1] = XCB_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!all_handled) {
|
||||
xcb_change_property(c,
|
||||
ev->requestor,
|
||||
ev->property,
|
||||
XCB_ATOM_ATOM,
|
||||
32,
|
||||
XCB_PROP_MODE_REPLACE,
|
||||
reply->value_len,
|
||||
reinterpret_cast<const void *>(atoms));
|
||||
}
|
||||
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (reply) {
|
||||
free(reply);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ev->property == XCB_NONE) { // obsolete client
|
||||
ev->property = ev->target;
|
||||
}
|
||||
|
||||
handled = handle_selection(ev->target, ev->property, ev->requestor);
|
||||
}
|
||||
|
||||
KXcbEvent<xcb_selection_notify_event_t> xev;
|
||||
xev.response_type = XCB_SELECTION_NOTIFY;
|
||||
xev.selection = ev->selection;
|
||||
xev.requestor = ev->requestor;
|
||||
xev.target = ev->target;
|
||||
xev.property = handled ? ev->property : XCB_NONE;
|
||||
|
||||
xcb_send_event(c, false, ev->requestor, 0, xev.buffer());
|
||||
}
|
||||
|
||||
bool KSelectionOwner::handle_selection(xcb_atom_t target_P, xcb_atom_t property_P, xcb_window_t requestor_P)
|
||||
{
|
||||
if (!d) {
|
||||
return false;
|
||||
}
|
||||
if (target_P == Private::xa_timestamp) {
|
||||
// qDebug() << "Handling timestamp request";
|
||||
xcb_change_property(d->connection,
|
||||
requestor_P,
|
||||
property_P,
|
||||
XCB_ATOM_INTEGER,
|
||||
32,
|
||||
XCB_PROP_MODE_REPLACE,
|
||||
1,
|
||||
reinterpret_cast<const void *>(&d->timestamp));
|
||||
} else if (target_P == Private::xa_targets) {
|
||||
replyTargets(property_P, requestor_P);
|
||||
} else if (genericReply(target_P, property_P, requestor_P)) {
|
||||
// handled
|
||||
} else {
|
||||
return false; // unknown
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KSelectionOwner::replyTargets(xcb_atom_t property_P, xcb_window_t requestor_P)
|
||||
{
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
xcb_atom_t atoms[3] = {Private::xa_multiple, Private::xa_timestamp, Private::xa_targets};
|
||||
|
||||
xcb_change_property(d->connection,
|
||||
requestor_P,
|
||||
property_P,
|
||||
XCB_ATOM_ATOM,
|
||||
32,
|
||||
XCB_PROP_MODE_REPLACE,
|
||||
sizeof(atoms) / sizeof(atoms[0]),
|
||||
reinterpret_cast<const void *>(atoms));
|
||||
|
||||
// qDebug() << "Handling targets request";
|
||||
}
|
||||
|
||||
bool KSelectionOwner::genericReply(xcb_atom_t, xcb_atom_t, xcb_window_t)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void KSelectionOwner::getAtoms()
|
||||
{
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
if (Private::manager_atom != XCB_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
xcb_connection_t *c = d->connection;
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
xcb_atom_t *atom;
|
||||
} atoms[] = {{"MANAGER", &Private::manager_atom},
|
||||
{"MULTIPLE", &Private::xa_multiple},
|
||||
{"TARGETS", &Private::xa_targets},
|
||||
{"TIMESTAMP", &Private::xa_timestamp}};
|
||||
|
||||
const int count = sizeof(atoms) / sizeof(atoms[0]);
|
||||
xcb_intern_atom_cookie_t cookies[count];
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
cookies[i] = xcb_intern_atom(c, false, strlen(atoms[i].name), atoms[i].name);
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, cookies[i], nullptr)) {
|
||||
*atoms[i].atom = reply->atom;
|
||||
free(reply);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xcb_atom_t KSelectionOwner::Private::manager_atom = XCB_NONE;
|
||||
xcb_atom_t KSelectionOwner::Private::xa_multiple = XCB_NONE;
|
||||
xcb_atom_t KSelectionOwner::Private::xa_targets = XCB_NONE;
|
||||
xcb_atom_t KSelectionOwner::Private::xa_timestamp = XCB_NONE;
|
||||
|
||||
#include "moc_kselectionowner.cpp"
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KSELECTIONOWNER_H
|
||||
#define KSELECTIONOWNER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <kwindowsystem_export.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
/**
|
||||
This class implements claiming and owning manager selections, as described
|
||||
in the ICCCM, section 2.8. The selection atom is passed to the constructor,
|
||||
claim() attempts to claim ownership of the selection, release() gives up
|
||||
the selection ownership. Signal lostOwnership() is emitted when the selection
|
||||
is claimed by another owner.
|
||||
@short ICCCM manager selection owner
|
||||
|
||||
This class is only useful on the xcb platform. On other platforms the code is only
|
||||
functional if the constructor overloads taking an xcb_connection_t are used. In case
|
||||
you inherit from this class ensure that you don't use xcb and/or XLib without verifying
|
||||
the platform.
|
||||
*/
|
||||
class KWINDOWSYSTEM_EXPORT KSelectionOwner : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* This constructor initializes the object, but doesn't perform any
|
||||
* operation on the selection.
|
||||
*
|
||||
* @param selection atom representing the manager selection
|
||||
* @param screen X screen, or -1 for default
|
||||
* @param parent parent object, or nullptr if there is none
|
||||
*/
|
||||
explicit KSelectionOwner(xcb_atom_t selection, int screen = -1, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* This constructor accepts the selection name and creates the appropriate atom
|
||||
* for it automatically.
|
||||
*
|
||||
* @param selection name of the manager selection
|
||||
* @param screen X screen, or -1 for default
|
||||
* @param parent parent object, or nullptr if there is none
|
||||
*/
|
||||
explicit KSelectionOwner(const char *selection, int screen = -1, QObject *parent = nullptr);
|
||||
/**
|
||||
* @overload
|
||||
* This constructor accepts the xcb_connection_t and root window and doesn't depend on
|
||||
* running on the xcb platform. Otherwise this constructor behaves like the similar one
|
||||
* without the xcb_connection_t.
|
||||
*
|
||||
* @param selection atom representing the manager selection
|
||||
* @param c the xcb connection this KSelectionWatcher should use
|
||||
* @param root the root window this KSelectionWatcher should use
|
||||
* @param parent parent object, or nullptr if there is none
|
||||
* @since 5.8
|
||||
**/
|
||||
explicit KSelectionOwner(xcb_atom_t selection, xcb_connection_t *c, xcb_window_t root, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* This constructor accepts the xcb_connection_t and root window and doesn't depend on
|
||||
* running on the xcb platform. Otherwise this constructor behaves like the similar one
|
||||
* without the xcb_connection_t.
|
||||
*
|
||||
* @param selection name of the manager selection
|
||||
* @param c the xcb connection this KSelectionWatcher should use
|
||||
* @param root the root window this KSelectionWatcher should use
|
||||
* @param parent parent object, or nullptr if there is none
|
||||
* @since 5.8
|
||||
**/
|
||||
explicit KSelectionOwner(const char *selection, xcb_connection_t *c, xcb_window_t root, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Destructor. Calls release().
|
||||
*/
|
||||
~KSelectionOwner() override;
|
||||
|
||||
/**
|
||||
* Try to claim ownership of the manager selection using the current X timestamp.
|
||||
*
|
||||
* This function returns immediately, but it may take up to one second for the claim
|
||||
* to succeed or fail, at which point either the claimedOwnership() or
|
||||
* failedToClaimOwnership() signal is emitted. The claim will not be completed until
|
||||
* the caller has returned to the event loop.
|
||||
*
|
||||
* If @p force is false, and the selection is already owned, the selection is not claimed,
|
||||
* and failedToClaimOwnership() is emitted. If @p force is true and the selection is
|
||||
* owned by another client, the client will be given one second to relinquish ownership
|
||||
* of the selection. If @p force_kill is true, and the previous owner fails to disown
|
||||
* the selection in time, it will be forcibly killed.
|
||||
*/
|
||||
void claim(bool force, bool force_kill = true);
|
||||
|
||||
/**
|
||||
* If the selection is owned, the ownership is given up.
|
||||
*/
|
||||
void release();
|
||||
|
||||
/**
|
||||
* If the selection is owned, returns the window used internally
|
||||
* for owning the selection.
|
||||
*/
|
||||
xcb_window_t ownerWindow() const; // None if not owning the selection
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
bool filterEvent(void *ev_P); // internal
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted if the selection was owned and the ownership
|
||||
* has been lost due to another client claiming it, this signal is emitted.
|
||||
* IMPORTANT: It's not safe to delete the instance in a slot connected
|
||||
* to this signal.
|
||||
*/
|
||||
void lostOwnership();
|
||||
|
||||
/**
|
||||
* This signal is emitted when claim() was successful in claiming
|
||||
* ownership of the selection.
|
||||
*/
|
||||
void claimedOwnership();
|
||||
|
||||
/**
|
||||
* This signal is emitted when claim() failed to claim ownership
|
||||
* of the selection.
|
||||
*/
|
||||
void failedToClaimOwnership();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Called for every X event received on the window used for owning
|
||||
* the selection. If true is returned, the event is filtered out.
|
||||
*/
|
||||
// virtual bool handleMessage( XEvent* ev ); // removed for KF5, please shout if you need this
|
||||
/**
|
||||
* Called when a SelectionRequest event is received. A reply should
|
||||
* be sent using the selection handling mechanism described in the ICCCM
|
||||
* section 2.
|
||||
*
|
||||
* @param target requested target type
|
||||
* @param property property to use for the reply data
|
||||
* @param requestor requestor window
|
||||
*/
|
||||
virtual bool genericReply(xcb_atom_t target, xcb_atom_t property, xcb_window_t requestor);
|
||||
/**
|
||||
* Called to announce the supported targets, as described in the ICCCM
|
||||
* section 2.6. The default implementation announces the required targets
|
||||
* MULTIPLE, TIMESTAMP and TARGETS.
|
||||
*/
|
||||
virtual void replyTargets(xcb_atom_t property, xcb_window_t requestor);
|
||||
/**
|
||||
* Called to create atoms needed for claiming the selection and
|
||||
* communication using the selection handling mechanism. The default
|
||||
* implementation must be called if reimplemented. This method
|
||||
* may be called repeatedly.
|
||||
*/
|
||||
virtual void getAtoms();
|
||||
/**
|
||||
* Sets extra data to be sent in the message sent to root window
|
||||
* after successfully claiming a selection. These extra data
|
||||
* are in data.l[3] and data.l[4] fields of the XClientMessage.
|
||||
*/
|
||||
void setData(uint32_t extra1, uint32_t extra2);
|
||||
|
||||
private:
|
||||
void filter_selection_request(void *ev_P);
|
||||
bool handle_selection(xcb_atom_t target_P, xcb_atom_t property_P, xcb_window_t requestor_P);
|
||||
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "kselectionwatcher.h"
|
||||
|
||||
#include "kwindowsystem.h"
|
||||
#include <config-kwindowsystem.h>
|
||||
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <private/qtx11extras_p.h>
|
||||
|
||||
static xcb_window_t get_selection_owner(xcb_connection_t *c, xcb_atom_t selection)
|
||||
{
|
||||
xcb_window_t owner = XCB_NONE;
|
||||
xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(c, xcb_get_selection_owner(c, selection), nullptr);
|
||||
|
||||
if (reply) {
|
||||
owner = reply->owner;
|
||||
free(reply);
|
||||
}
|
||||
|
||||
return owner;
|
||||
}
|
||||
|
||||
static xcb_atom_t intern_atom(xcb_connection_t *c, const char *name)
|
||||
{
|
||||
xcb_atom_t atom = XCB_NONE;
|
||||
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, xcb_intern_atom(c, false, strlen(name), name), nullptr);
|
||||
|
||||
if (reply) {
|
||||
atom = reply->atom;
|
||||
free(reply);
|
||||
}
|
||||
|
||||
return atom;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
// KSelectionWatcher
|
||||
//*******************************************
|
||||
|
||||
class Q_DECL_HIDDEN KSelectionWatcher::Private : public QAbstractNativeEventFilter
|
||||
{
|
||||
public:
|
||||
Private(KSelectionWatcher *watcher_P, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
|
||||
: connection(c)
|
||||
, root(root)
|
||||
, selection(selection_P)
|
||||
, selection_owner(XCB_NONE)
|
||||
, watcher(watcher_P)
|
||||
{
|
||||
QCoreApplication::instance()->installNativeEventFilter(this);
|
||||
}
|
||||
|
||||
xcb_connection_t *connection;
|
||||
xcb_window_t root;
|
||||
const xcb_atom_t selection;
|
||||
xcb_window_t selection_owner;
|
||||
static xcb_atom_t manager_atom;
|
||||
|
||||
static Private *create(KSelectionWatcher *watcher, xcb_atom_t selection_P, int screen_P);
|
||||
static Private *create(KSelectionWatcher *watcher, const char *selection_P, int screen_P);
|
||||
static Private *create(KSelectionWatcher *watcher, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root);
|
||||
static Private *create(KSelectionWatcher *watcher, const char *selection_P, xcb_connection_t *c, xcb_window_t root);
|
||||
|
||||
protected:
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override
|
||||
{
|
||||
if (eventType != "xcb_generic_event_t") {
|
||||
return false;
|
||||
}
|
||||
watcher->filterEvent(message);
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
KSelectionWatcher *watcher;
|
||||
};
|
||||
|
||||
KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, xcb_atom_t selection_P, int screen_P)
|
||||
{
|
||||
if (KWindowSystem::isPlatformX11()) {
|
||||
return create(watcher, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
|
||||
{
|
||||
return new Private(watcher, selection_P, c, root);
|
||||
}
|
||||
|
||||
KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, const char *selection_P, int screen_P)
|
||||
{
|
||||
if (KWindowSystem::isPlatformX11()) {
|
||||
return create(watcher, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, const char *selection_P, xcb_connection_t *c, xcb_window_t root)
|
||||
{
|
||||
return new Private(watcher, intern_atom(c, selection_P), c, root);
|
||||
}
|
||||
|
||||
KSelectionWatcher::KSelectionWatcher(xcb_atom_t selection_P, int screen_P, QObject *parent_P)
|
||||
: QObject(parent_P)
|
||||
, d(Private::create(this, selection_P, screen_P))
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
KSelectionWatcher::KSelectionWatcher(const char *selection_P, int screen_P, QObject *parent_P)
|
||||
: QObject(parent_P)
|
||||
, d(Private::create(this, selection_P, screen_P))
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
KSelectionWatcher::KSelectionWatcher(xcb_atom_t selection, xcb_connection_t *c, xcb_window_t root, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(Private::create(this, selection, c, root))
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
KSelectionWatcher::KSelectionWatcher(const char *selection, xcb_connection_t *c, xcb_window_t root, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(Private::create(this, selection, c, root))
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
KSelectionWatcher::~KSelectionWatcher()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void KSelectionWatcher::init()
|
||||
{
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
if (Private::manager_atom == XCB_NONE) {
|
||||
xcb_connection_t *c = d->connection;
|
||||
|
||||
xcb_intern_atom_cookie_t atom_cookie = xcb_intern_atom(c, false, strlen("MANAGER"), "MANAGER");
|
||||
xcb_get_window_attributes_cookie_t attr_cookie = xcb_get_window_attributes(c, d->root);
|
||||
|
||||
xcb_intern_atom_reply_t *atom_reply = xcb_intern_atom_reply(c, atom_cookie, nullptr);
|
||||
Private::manager_atom = atom_reply->atom;
|
||||
free(atom_reply);
|
||||
|
||||
xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(c, attr_cookie, nullptr);
|
||||
uint32_t event_mask = attr->your_event_mask;
|
||||
free(attr);
|
||||
|
||||
if (!(event_mask & XCB_EVENT_MASK_STRUCTURE_NOTIFY)) {
|
||||
// We need XCB_EVENT_MASK_STRUCTURE_NORITY on the root window
|
||||
event_mask |= XCB_EVENT_MASK_STRUCTURE_NOTIFY;
|
||||
xcb_change_window_attributes(c, d->root, XCB_CW_EVENT_MASK, &event_mask);
|
||||
}
|
||||
}
|
||||
|
||||
owner(); // trigger reading of current selection status
|
||||
}
|
||||
|
||||
xcb_window_t KSelectionWatcher::owner()
|
||||
{
|
||||
if (!d) {
|
||||
return XCB_WINDOW_NONE;
|
||||
}
|
||||
xcb_connection_t *c = d->connection;
|
||||
|
||||
xcb_window_t current_owner = get_selection_owner(c, d->selection);
|
||||
if (current_owner == XCB_NONE) {
|
||||
return XCB_NONE;
|
||||
}
|
||||
|
||||
if (current_owner == d->selection_owner) {
|
||||
return d->selection_owner;
|
||||
}
|
||||
|
||||
// We have a new selection owner - select for structure notify events
|
||||
uint32_t mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
|
||||
xcb_void_cookie_t cookie = xcb_change_window_attributes_checked(c, current_owner, XCB_CW_EVENT_MASK, &mask);
|
||||
|
||||
// Verify that the owner didn't change again while selecting for events
|
||||
xcb_window_t new_owner = get_selection_owner(c, d->selection);
|
||||
xcb_generic_error_t *err = xcb_request_check(c, cookie);
|
||||
|
||||
if (!err && current_owner == new_owner) {
|
||||
d->selection_owner = current_owner;
|
||||
Q_EMIT newOwner(d->selection_owner);
|
||||
} else {
|
||||
// ### This doesn't look right - the selection could have an owner
|
||||
d->selection_owner = XCB_NONE;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
free(err);
|
||||
}
|
||||
|
||||
return d->selection_owner;
|
||||
}
|
||||
|
||||
void KSelectionWatcher::filterEvent(void *ev_P)
|
||||
{
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t *>(ev_P);
|
||||
const uint response_type = event->response_type & ~0x80;
|
||||
if (response_type == XCB_CLIENT_MESSAGE) {
|
||||
xcb_client_message_event_t *cm_event = reinterpret_cast<xcb_client_message_event_t *>(event);
|
||||
|
||||
if (cm_event->type != Private::manager_atom || cm_event->data.data32[1] != d->selection) {
|
||||
return;
|
||||
}
|
||||
// owner() checks whether the owner changed and emits newOwner()
|
||||
owner();
|
||||
return;
|
||||
}
|
||||
if (response_type == XCB_DESTROY_NOTIFY) {
|
||||
xcb_destroy_notify_event_t *ev = reinterpret_cast<xcb_destroy_notify_event_t *>(event);
|
||||
if (d->selection_owner == XCB_NONE || ev->window != d->selection_owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->selection_owner = XCB_NONE; // in case the exactly same ID gets reused as the owner
|
||||
|
||||
if (owner() == XCB_NONE) {
|
||||
Q_EMIT lostOwner(); // it must be safe to delete 'this' in a slot
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
xcb_atom_t KSelectionWatcher::Private::manager_atom = XCB_NONE;
|
||||
|
||||
#include "moc_kselectionwatcher.cpp"
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KSELECTIONWATCHER_H
|
||||
#define KSELECTIONWATCHER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <kwindowsystem_export.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
/**
|
||||
This class implements watching manager selections, as described in the ICCCM
|
||||
section 2.8. It emits signal newOwner() when a new owner claim the selection,
|
||||
and emits lostOwner() when the selection ownership is given up. To find
|
||||
out current owner of the selection, owner() can be used.
|
||||
@short ICCCM manager selection watching
|
||||
|
||||
This class is only useful on the xcb platform. On other platforms the code is only
|
||||
functional if the constructor overloads taking an xcb_connection_t are used. In case
|
||||
you inherit from this class ensure that you don't use xcb and/or XLib without verifying
|
||||
the platform.
|
||||
*/
|
||||
class KWINDOWSYSTEM_EXPORT KSelectionWatcher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* This constructor initializes the object, but doesn't perform any
|
||||
* operation on the selection.
|
||||
*
|
||||
* @param selection atom representing the manager selection
|
||||
* @param screen X screen, or -1 for default
|
||||
* @param parent parent object, or nullptr if there is none
|
||||
*/
|
||||
explicit KSelectionWatcher(xcb_atom_t selection, int screen = -1, QObject *parent = nullptr);
|
||||
/**
|
||||
* @overload
|
||||
* This constructor accepts the selection name and creates the appropriate atom
|
||||
* for it automatically.
|
||||
*
|
||||
* @param selection name of the manager selection
|
||||
* @param screen X screen, or -1 for default
|
||||
* @param parent parent object, or nullptr if there is none
|
||||
*/
|
||||
explicit KSelectionWatcher(const char *selection, int screen = -1, QObject *parent = nullptr);
|
||||
/**
|
||||
* @overload
|
||||
* This constructor accepts the xcb_connection_t and root window and doesn't depend on
|
||||
* running on the xcb platform. Otherwise this constructor behaves like the similar one
|
||||
* without the xcb_connection_t.
|
||||
*
|
||||
* @param selection atom representing the manager selection
|
||||
* @param c the xcb connection this KSelectionWatcher should use
|
||||
* @param root the root window this KSelectionWatcher should use
|
||||
* @since 5.8
|
||||
**/
|
||||
explicit KSelectionWatcher(xcb_atom_t selection, xcb_connection_t *c, xcb_window_t root, QObject *parent = nullptr);
|
||||
/**
|
||||
* @overload
|
||||
* This constructor accepts the xcb_connection_t and root window and doesn't depend on
|
||||
* running on the xcb platform. Otherwise this constructor behaves like the similar one
|
||||
* without the xcb_connection_t.
|
||||
*
|
||||
* @param selection name of the manager selection
|
||||
* @param c the xcb connection this KSelectionWatcher should use
|
||||
* @param root the root window this KSelectionWatcher should use
|
||||
* @since 5.8
|
||||
**/
|
||||
explicit KSelectionWatcher(const char *selection, xcb_connection_t *c, xcb_window_t root, QObject *parent = nullptr);
|
||||
~KSelectionWatcher() override;
|
||||
/**
|
||||
* Return the current owner of the manager selection, if any. Note that if the event
|
||||
* informing about the owner change is still in the input queue, newOwner() might
|
||||
* have been emitted yet.
|
||||
*/
|
||||
xcb_window_t owner();
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
void filterEvent(void *ev_P); // internal
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted when the selection is successfully claimed by a new
|
||||
* owner.
|
||||
* @param owner the new owner of the selection
|
||||
*/
|
||||
void newOwner(xcb_window_t owner);
|
||||
/**
|
||||
* This signal is emitted when the selection is given up, i.e. there's no
|
||||
* owner. Note that the selection may be immediately claimed again,
|
||||
* so the newOwner() signal may be emitted right after this one.
|
||||
* It's safe to delete the instance in a slot connected to this signal.
|
||||
*/
|
||||
void lostOwner();
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(xcb_window_t)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2009 Marco Martin <notmart@gmail.com>
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "kwindoweffects_x11.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QVarLengthArray>
|
||||
|
||||
#include "kx11extras.h"
|
||||
#include <config-kwindowsystem.h>
|
||||
|
||||
#include <QMatrix4x4>
|
||||
#include <QWindow>
|
||||
#include <private/qtx11extras_p.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include "cptr_p.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace KWindowEffects;
|
||||
|
||||
KWindowEffectsPrivateX11::KWindowEffectsPrivateX11()
|
||||
{
|
||||
}
|
||||
|
||||
KWindowEffectsPrivateX11::~KWindowEffectsPrivateX11()
|
||||
{
|
||||
}
|
||||
|
||||
bool KWindowEffectsPrivateX11::isEffectAvailable(Effect effect)
|
||||
{
|
||||
if (!KX11Extras::self()->compositingActive()) {
|
||||
return false;
|
||||
}
|
||||
QByteArray effectName;
|
||||
|
||||
switch (effect) {
|
||||
case Slide:
|
||||
effectName = QByteArrayLiteral("_KDE_SLIDE");
|
||||
break;
|
||||
case BlurBehind:
|
||||
effectName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION");
|
||||
break;
|
||||
case BackgroundContrast:
|
||||
effectName = QByteArrayLiteral("_KDE_NET_WM_BACKGROUND_CONTRAST_REGION");
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// hackish way to find out if KWin has the effect enabled,
|
||||
// TODO provide proper support
|
||||
xcb_connection_t *c = QX11Info::connection();
|
||||
xcb_list_properties_cookie_t propsCookie = xcb_list_properties_unchecked(c, QX11Info::appRootWindow());
|
||||
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
|
||||
|
||||
UniqueCPointer<xcb_list_properties_reply_t> props(xcb_list_properties_reply(c, propsCookie, nullptr));
|
||||
UniqueCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
|
||||
if (!atom || !props) {
|
||||
return false;
|
||||
}
|
||||
xcb_atom_t *atoms = xcb_list_properties_atoms(props.get());
|
||||
for (int i = 0; i < props->atoms_len; ++i) {
|
||||
if (atoms[i] == atom->atom) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void KWindowEffectsPrivateX11::slideWindow(QWindow *window, SlideFromLocation location, int offset)
|
||||
{
|
||||
xcb_connection_t *c = QX11Info::connection();
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray effectName = QByteArrayLiteral("_KDE_SLIDE");
|
||||
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
|
||||
|
||||
const int size = 2;
|
||||
int32_t data[size];
|
||||
data[0] = offset;
|
||||
|
||||
switch (location) {
|
||||
case LeftEdge:
|
||||
data[1] = 0;
|
||||
break;
|
||||
case TopEdge:
|
||||
data[1] = 1;
|
||||
break;
|
||||
case RightEdge:
|
||||
data[1] = 2;
|
||||
break;
|
||||
case BottomEdge:
|
||||
data[1] = 3;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
UniqueCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
|
||||
if (!atom) {
|
||||
return;
|
||||
}
|
||||
if (location == NoEdge) {
|
||||
xcb_delete_property(c, window->winId(), atom->atom);
|
||||
} else {
|
||||
xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, atom->atom, 32, size, data);
|
||||
}
|
||||
}
|
||||
|
||||
void KWindowEffectsPrivateX11::enableBlurBehind(QWindow *window, bool enable, const QRegion ®ion)
|
||||
{
|
||||
xcb_connection_t *c = QX11Info::connection();
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION");
|
||||
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
|
||||
UniqueCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
|
||||
if (!atom) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
QList<uint32_t> data;
|
||||
data.reserve(region.rectCount() * 4);
|
||||
for (const QRect &r : region) {
|
||||
// kwin on X uses device pixels, convert from logical
|
||||
auto dpr = qApp->devicePixelRatio();
|
||||
data << std::floor(r.x() * dpr) << std::floor(r.y() * dpr) << std::ceil(r.width() * dpr) << std::ceil(r.height() * dpr);
|
||||
}
|
||||
|
||||
xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData());
|
||||
} else {
|
||||
xcb_delete_property(c, window->winId(), atom->atom);
|
||||
}
|
||||
}
|
||||
|
||||
void KWindowEffectsPrivateX11::enableBackgroundContrast(QWindow *window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion ®ion)
|
||||
{
|
||||
xcb_connection_t *c = QX11Info::connection();
|
||||
const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_BACKGROUND_CONTRAST_REGION");
|
||||
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
|
||||
UniqueCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
|
||||
if (!atom) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
QList<uint32_t> data;
|
||||
data.reserve(region.rectCount() * 4 + 16);
|
||||
for (const QRect &r : region) {
|
||||
auto dpr = qApp->devicePixelRatio();
|
||||
data << std::floor(r.x() * dpr) << std::floor(r.y() * dpr) << std::ceil(r.width() * dpr) << std::ceil(r.height() * dpr);
|
||||
}
|
||||
|
||||
QMatrix4x4 satMatrix; // saturation
|
||||
QMatrix4x4 intMatrix; // intensity
|
||||
QMatrix4x4 contMatrix; // contrast
|
||||
|
||||
// clang-format off
|
||||
|
||||
//Saturation matrix
|
||||
if (!qFuzzyCompare(saturation, 1.0)) {
|
||||
const qreal rval = (1.0 - saturation) * .2126;
|
||||
const qreal gval = (1.0 - saturation) * .7152;
|
||||
const qreal bval = (1.0 - saturation) * .0722;
|
||||
|
||||
satMatrix = QMatrix4x4(rval + saturation, rval, rval, 0.0,
|
||||
gval, gval + saturation, gval, 0.0,
|
||||
bval, bval, bval + saturation, 0.0,
|
||||
0, 0, 0, 1.0);
|
||||
}
|
||||
|
||||
//IntensityMatrix
|
||||
if (!qFuzzyCompare(intensity, 1.0)) {
|
||||
intMatrix.scale(intensity, intensity, intensity);
|
||||
}
|
||||
|
||||
//Contrast Matrix
|
||||
if (!qFuzzyCompare(contrast, 1.0)) {
|
||||
const float transl = (1.0 - contrast) / 2.0;
|
||||
|
||||
contMatrix = QMatrix4x4(contrast, 0, 0, 0.0,
|
||||
0, contrast, 0, 0.0,
|
||||
0, 0, contrast, 0.0,
|
||||
transl, transl, transl, 1.0);
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
QMatrix4x4 colorMatrix = contMatrix * satMatrix * intMatrix;
|
||||
colorMatrix = colorMatrix.transposed();
|
||||
|
||||
uint32_t *rawData = reinterpret_cast<uint32_t *>(colorMatrix.data());
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
data << rawData[i];
|
||||
}
|
||||
|
||||
xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, atom->atom, 32, data.size(), data.constData());
|
||||
} else {
|
||||
xcb_delete_property(c, window->winId(), atom->atom);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KWINDOWEFFECTS_X11_H
|
||||
#define KWINDOWEFFECTS_X11_H
|
||||
#include "kwindoweffects_p.h"
|
||||
|
||||
class KWindowEffectsPrivateX11 : public KWindowEffectsPrivate
|
||||
{
|
||||
public:
|
||||
KWindowEffectsPrivateX11();
|
||||
~KWindowEffectsPrivateX11() override;
|
||||
bool isEffectAvailable(KWindowEffects::Effect effect) override;
|
||||
void slideWindow(QWindow *window, KWindowEffects::SlideFromLocation location, int offset) override;
|
||||
void enableBlurBehind(QWindow *window, bool enable = true, const QRegion ®ion = QRegion()) override;
|
||||
void enableBackgroundContrast(QWindow *window,
|
||||
bool enable = true,
|
||||
qreal contrast = 1,
|
||||
qreal intensity = 1,
|
||||
qreal saturation = 1,
|
||||
const QRegion ®ion = QRegion()) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "kwindowshadow_p_x11.h"
|
||||
|
||||
#include <private/qtx11extras_p.h>
|
||||
|
||||
static const QByteArray s_atomName = QByteArrayLiteral("_KDE_NET_WM_SHADOW");
|
||||
|
||||
bool KWindowShadowTilePrivateX11::create()
|
||||
{
|
||||
xcb_connection_t *connection = QX11Info::connection();
|
||||
xcb_window_t rootWindow = QX11Info::appRootWindow();
|
||||
|
||||
const uint16_t width = uint16_t(image.width());
|
||||
const uint16_t height = uint16_t(image.height());
|
||||
const uint8_t depth = uint8_t(image.depth());
|
||||
|
||||
pixmap = xcb_generate_id(connection);
|
||||
gc = xcb_generate_id(connection);
|
||||
|
||||
xcb_create_pixmap(connection, depth, pixmap, rootWindow, width, height);
|
||||
xcb_create_gc(connection, gc, pixmap, 0, nullptr);
|
||||
|
||||
xcb_put_image(connection, //
|
||||
XCB_IMAGE_FORMAT_Z_PIXMAP,
|
||||
pixmap,
|
||||
gc,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
depth,
|
||||
image.sizeInBytes(),
|
||||
image.constBits());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KWindowShadowTilePrivateX11::destroy()
|
||||
{
|
||||
xcb_connection_t *connection = QX11Info::connection();
|
||||
if (connection) {
|
||||
xcb_free_pixmap(connection, pixmap);
|
||||
xcb_free_gc(connection, gc);
|
||||
}
|
||||
pixmap = XCB_PIXMAP_NONE;
|
||||
gc = XCB_NONE;
|
||||
}
|
||||
|
||||
KWindowShadowTilePrivateX11 *KWindowShadowTilePrivateX11::get(const KWindowShadowTile *tile)
|
||||
{
|
||||
KWindowShadowTilePrivate *d = KWindowShadowTilePrivate::get(tile);
|
||||
return static_cast<KWindowShadowTilePrivateX11 *>(d);
|
||||
}
|
||||
|
||||
static xcb_atom_t lookupAtom(const QByteArray &atomName)
|
||||
{
|
||||
xcb_connection_t *connection = QX11Info::connection();
|
||||
if (!connection) {
|
||||
return XCB_ATOM_NONE;
|
||||
}
|
||||
|
||||
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(connection, //
|
||||
false,
|
||||
atomName.size(),
|
||||
atomName.constData());
|
||||
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, atomCookie, nullptr);
|
||||
|
||||
if (!reply) {
|
||||
return XCB_ATOM_NONE;
|
||||
}
|
||||
|
||||
xcb_atom_t atom = reply->atom;
|
||||
free(reply);
|
||||
|
||||
return atom;
|
||||
}
|
||||
|
||||
static xcb_pixmap_t nativeHandleForTile(const KWindowShadowTile::Ptr &tile)
|
||||
{
|
||||
const auto d = KWindowShadowTilePrivateX11::get(tile.data());
|
||||
return d->pixmap;
|
||||
}
|
||||
|
||||
bool KWindowShadowPrivateX11::create()
|
||||
{
|
||||
xcb_connection_t *connection = QX11Info::connection();
|
||||
|
||||
const xcb_atom_t atom = lookupAtom(s_atomName);
|
||||
if (atom == XCB_ATOM_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QList<quint32> data(12);
|
||||
int i = 0;
|
||||
|
||||
// Unfortunately we cannot use handle of XCB_PIXMAP_NONE for missing shadow tiles because
|
||||
// KWin expects **all** shadow tile handles to be valid. Maybe we could address this small
|
||||
// inconvenience and then remove the empty tile stuff.
|
||||
|
||||
if (topTile) {
|
||||
data[i++] = nativeHandleForTile(topTile);
|
||||
} else {
|
||||
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
|
||||
}
|
||||
|
||||
if (topRightTile) {
|
||||
data[i++] = nativeHandleForTile(topRightTile);
|
||||
} else {
|
||||
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
|
||||
}
|
||||
|
||||
if (rightTile) {
|
||||
data[i++] = nativeHandleForTile(rightTile);
|
||||
} else {
|
||||
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
|
||||
}
|
||||
|
||||
if (bottomRightTile) {
|
||||
data[i++] = nativeHandleForTile(bottomRightTile);
|
||||
} else {
|
||||
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
|
||||
}
|
||||
|
||||
if (bottomTile) {
|
||||
data[i++] = nativeHandleForTile(bottomTile);
|
||||
} else {
|
||||
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
|
||||
}
|
||||
|
||||
if (bottomLeftTile) {
|
||||
data[i++] = nativeHandleForTile(bottomLeftTile);
|
||||
} else {
|
||||
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
|
||||
}
|
||||
|
||||
if (leftTile) {
|
||||
data[i++] = nativeHandleForTile(leftTile);
|
||||
} else {
|
||||
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
|
||||
}
|
||||
|
||||
if (topLeftTile) {
|
||||
data[i++] = nativeHandleForTile(topLeftTile);
|
||||
} else {
|
||||
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
|
||||
}
|
||||
|
||||
if (topLeftTile || topTile || topRightTile) {
|
||||
data[i++] = uint32_t(padding.top());
|
||||
} else {
|
||||
data[i++] = 1;
|
||||
}
|
||||
|
||||
if (topRightTile || rightTile || bottomRightTile) {
|
||||
data[i++] = uint32_t(padding.right());
|
||||
} else {
|
||||
data[i++] = 1;
|
||||
}
|
||||
|
||||
if (bottomRightTile || bottomTile || bottomLeftTile) {
|
||||
data[i++] = uint32_t(padding.bottom());
|
||||
} else {
|
||||
data[i++] = 1;
|
||||
}
|
||||
|
||||
if (bottomLeftTile || leftTile || topLeftTile) {
|
||||
data[i++] = uint32_t(padding.left());
|
||||
} else {
|
||||
data[i++] = 1;
|
||||
}
|
||||
|
||||
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window->winId(), atom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData());
|
||||
xcb_flush(connection);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KWindowShadowPrivateX11::destroy()
|
||||
{
|
||||
emptyTile = nullptr;
|
||||
|
||||
// For some reason, QWindow changes visibility of QSurface::surfaceHandle().
|
||||
const QSurface *surface = window;
|
||||
|
||||
// Attempting to uninstall the shadow after the platform window had been destroyed.
|
||||
if (!(surface && surface->surfaceHandle())) {
|
||||
return;
|
||||
}
|
||||
|
||||
xcb_connection_t *connection = QX11Info::connection();
|
||||
|
||||
const xcb_atom_t atom = lookupAtom(s_atomName);
|
||||
if (atom == XCB_ATOM_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
xcb_delete_property(connection, window->winId(), atom);
|
||||
}
|
||||
|
||||
KWindowShadowTile::Ptr KWindowShadowPrivateX11::getOrCreateEmptyTile()
|
||||
{
|
||||
if (!emptyTile) {
|
||||
QImage image(QSize(1, 1), QImage::Format_ARGB32);
|
||||
image.fill(Qt::transparent);
|
||||
|
||||
emptyTile = KWindowShadowTile::Ptr::create();
|
||||
emptyTile->setImage(image);
|
||||
emptyTile->create();
|
||||
}
|
||||
|
||||
return emptyTile;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef KWINDOWSHADOW_P_X11_H
|
||||
#define KWINDOWSHADOW_P_X11_H
|
||||
|
||||
#include "kwindowshadow_p.h"
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
class KWindowShadowTilePrivateX11 final : public KWindowShadowTilePrivate
|
||||
{
|
||||
public:
|
||||
bool create() override;
|
||||
void destroy() override;
|
||||
|
||||
static KWindowShadowTilePrivateX11 *get(const KWindowShadowTile *tile);
|
||||
|
||||
xcb_pixmap_t pixmap = XCB_PIXMAP_NONE;
|
||||
xcb_gcontext_t gc = XCB_NONE;
|
||||
};
|
||||
|
||||
class KWindowShadowPrivateX11 final : public KWindowShadowPrivate
|
||||
{
|
||||
public:
|
||||
bool create() override;
|
||||
void destroy() override;
|
||||
|
||||
KWindowShadowTile::Ptr getOrCreateEmptyTile();
|
||||
|
||||
KWindowShadowTile::Ptr emptyTile;
|
||||
};
|
||||
|
||||
#endif // KWINDOWSHADOW_P_X11_H
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Matthias Ettrich <ettrich@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 Lubos Lunak <l.lunak@kde.org>
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "kwindowsystem_p_x11.h"
|
||||
|
||||
#include "kx11extras.h"
|
||||
|
||||
void KWindowSystemPrivateX11::activateWindow(QWindow *win, long time)
|
||||
{
|
||||
KX11Extras::activateWindow(win->winId(), time);
|
||||
}
|
||||
|
||||
bool KWindowSystemPrivateX11::showingDesktop()
|
||||
{
|
||||
return KX11Extras::showingDesktop();
|
||||
}
|
||||
|
||||
void KWindowSystemPrivateX11::setShowingDesktop(bool showing)
|
||||
{
|
||||
KX11Extras::setShowingDesktop(showing);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KWINDOWSYSTEM_P_X11_H
|
||||
#define KWINDOWSYSTEM_P_X11_H
|
||||
|
||||
#include "kwindowsystem_p.h"
|
||||
|
||||
class KWindowSystemPrivateX11 : public KWindowSystemPrivate
|
||||
{
|
||||
public:
|
||||
void activateWindow(QWindow *win, long time) override;
|
||||
bool showingDesktop() override;
|
||||
void setShowingDesktop(bool showing) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KXERRORHANDLER_H
|
||||
#define KXERRORHANDLER_H
|
||||
#include <config-kwindowsystem.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <private/qtx11extras_p.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
class KXErrorHandlerPrivate;
|
||||
/**
|
||||
* This class simplifies handling of X errors. It shouldn't be necessary to use
|
||||
* with Qt classes, as the toolkit should handle X errors itself, so this
|
||||
* class will be mainly used with direct Xlib usage, and some lowlevel classes
|
||||
* like NETWinInfo.
|
||||
*
|
||||
* The usual usage is to create a KXErrorHandler instance right before starting
|
||||
* operations that might cause X errors, and checking if there was an error
|
||||
* by calling error() after the operations are finished. The handlers
|
||||
* may be nested, but must be destroyed in reverse order they were created.
|
||||
*
|
||||
* There's no need to do X sync before creating an instance, every instance
|
||||
* will handle only errors for request issued after the instance was created.
|
||||
* Errors for older requests will be passed to previous error handler.
|
||||
* When checking for error by calling error() at the end, it is necessary
|
||||
* to sync with X, to catch all errors that were caused by requests issued
|
||||
* before the call to error(). This can be done by passing true to error()
|
||||
* to cause explicit XSync(), however, if the last X request needed a roundtrip
|
||||
* (e.g. XGetWindowAttributes(), XGetGeometry(), etc.), it is not required
|
||||
* to do an explicit sync.
|
||||
*
|
||||
* @author Lubos Lunak <l.lunak@kde.org>
|
||||
* @short Handler for X errors
|
||||
*/
|
||||
class KXErrorHandler
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates error handler that will set error flag after encountering
|
||||
* any X error.
|
||||
*/
|
||||
explicit KXErrorHandler(Display *dpy = QX11Info::display());
|
||||
/**
|
||||
* This constructor takes pointer to a function whose prototype matches
|
||||
* the one that's used with the XSetErrorHandler() Xlib function.
|
||||
* NOTE: For the error flag to be set, the function must return a non-zero
|
||||
* value.
|
||||
*/
|
||||
explicit KXErrorHandler(int (*handler)(Display *, XErrorEvent *), Display *dpy = QX11Info::display());
|
||||
/**
|
||||
* This function returns true if the error flag is set (i.e. no custom handler
|
||||
* function was used and there was any error, or the custom handler indicated
|
||||
* an error by its return value).
|
||||
*
|
||||
* @param sync if true, an explicit XSync() will be done. Not necessary
|
||||
* when the last X request required a roundtrip.
|
||||
*/
|
||||
bool error(bool sync) const;
|
||||
/**
|
||||
* This function returns the error event for the first X error that occurred.
|
||||
* The return value is useful only if error() returned true.
|
||||
* @since 4.0.1
|
||||
*/
|
||||
XErrorEvent errorEvent() const;
|
||||
/**
|
||||
* Returns error message for the given error. The error message is not translated,
|
||||
* as it is meant for debugging.
|
||||
* @since 4.0.1
|
||||
*/
|
||||
static QByteArray errorMessage(const XErrorEvent &e, Display *dpy = QX11Info::display());
|
||||
~KXErrorHandler();
|
||||
|
||||
private:
|
||||
void addHandler();
|
||||
int handle(Display *dpy, XErrorEvent *e);
|
||||
bool (*user_handler1)(int request, int error_code, unsigned long resource_id);
|
||||
int (*user_handler2)(Display *, XErrorEvent *);
|
||||
int (*old_handler)(Display *, XErrorEvent *);
|
||||
static int handler_wrapper(Display *, XErrorEvent *);
|
||||
static KXErrorHandler **handlers;
|
||||
static int pos;
|
||||
static int size;
|
||||
Q_DISABLE_COPY(KXErrorHandler)
|
||||
KXErrorHandlerPrivate *const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2001-2003 Lubos Lunak <l.lunak@kde.org>
|
||||
SPDX-FileCopyrightText: 2012 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "kxmessages.h"
|
||||
#include "cptr_p.h"
|
||||
#include "kxutils_p.h"
|
||||
#include "kxcbevent_p.h"
|
||||
|
||||
#if KWINDOWSYSTEM_HAVE_X11
|
||||
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QWindow> // WId
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include <private/qtx11extras_p.h>
|
||||
|
||||
class XcbAtom
|
||||
{
|
||||
public:
|
||||
explicit XcbAtom(const QByteArray &name, bool onlyIfExists = false)
|
||||
: m_name(name)
|
||||
, m_atom(XCB_ATOM_NONE)
|
||||
, m_connection(nullptr)
|
||||
, m_retrieved(false)
|
||||
, m_onlyIfExists(onlyIfExists)
|
||||
{
|
||||
m_cookie.sequence = 0;
|
||||
}
|
||||
explicit XcbAtom(xcb_connection_t *c, const QByteArray &name, bool onlyIfExists = false)
|
||||
: m_name(name)
|
||||
, m_atom(XCB_ATOM_NONE)
|
||||
, m_cookie(xcb_intern_atom_unchecked(c, onlyIfExists, name.length(), name.constData()))
|
||||
, m_connection(c)
|
||||
, m_retrieved(false)
|
||||
, m_onlyIfExists(onlyIfExists)
|
||||
{
|
||||
}
|
||||
|
||||
~XcbAtom()
|
||||
{
|
||||
if (!m_retrieved && m_cookie.sequence && m_connection) {
|
||||
xcb_discard_reply(m_connection, m_cookie.sequence);
|
||||
}
|
||||
}
|
||||
|
||||
operator xcb_atom_t()
|
||||
{
|
||||
getReply();
|
||||
return m_atom;
|
||||
}
|
||||
|
||||
inline const QByteArray &name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
inline void setConnection(xcb_connection_t *c)
|
||||
{
|
||||
m_connection = c;
|
||||
}
|
||||
|
||||
inline void fetch()
|
||||
{
|
||||
if (!m_connection || m_name.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
m_cookie = xcb_intern_atom_unchecked(m_connection, m_onlyIfExists, m_name.length(), m_name.constData());
|
||||
}
|
||||
|
||||
private:
|
||||
void getReply()
|
||||
{
|
||||
if (m_retrieved || !m_cookie.sequence || !m_connection) {
|
||||
return;
|
||||
}
|
||||
UniqueCPointer<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(m_connection, m_cookie, nullptr));
|
||||
if (reply) {
|
||||
m_atom = reply->atom;
|
||||
}
|
||||
m_retrieved = true;
|
||||
}
|
||||
QByteArray m_name;
|
||||
xcb_atom_t m_atom;
|
||||
xcb_intern_atom_cookie_t m_cookie;
|
||||
xcb_connection_t *m_connection;
|
||||
bool m_retrieved;
|
||||
bool m_onlyIfExists;
|
||||
};
|
||||
|
||||
class KXMessagesPrivate : public QAbstractNativeEventFilter
|
||||
{
|
||||
public:
|
||||
KXMessagesPrivate(KXMessages *parent, const char *acceptBroadcast, xcb_connection_t *c, xcb_window_t root)
|
||||
: accept_atom1(acceptBroadcast ? QByteArray(acceptBroadcast) + QByteArrayLiteral("_BEGIN") : QByteArray())
|
||||
, accept_atom2(acceptBroadcast ? QByteArray(acceptBroadcast) : QByteArray())
|
||||
, handle(new QWindow)
|
||||
, q(parent)
|
||||
, valid(c)
|
||||
, connection(c)
|
||||
, rootWindow(root)
|
||||
{
|
||||
if (acceptBroadcast) {
|
||||
accept_atom1.setConnection(c);
|
||||
accept_atom1.fetch();
|
||||
accept_atom2.setConnection(c);
|
||||
accept_atom2.fetch();
|
||||
QCoreApplication::instance()->installNativeEventFilter(this);
|
||||
}
|
||||
}
|
||||
XcbAtom accept_atom1;
|
||||
XcbAtom accept_atom2;
|
||||
QMap<WId, QByteArray> incoming_messages;
|
||||
std::unique_ptr<QWindow> handle;
|
||||
KXMessages *q;
|
||||
bool valid;
|
||||
xcb_connection_t *connection;
|
||||
xcb_window_t rootWindow;
|
||||
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override
|
||||
{
|
||||
// A faster comparison than eventType != "xcb_generic_event_t"
|
||||
if (eventType[0] != 'x') {
|
||||
return false;
|
||||
}
|
||||
xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t *>(message);
|
||||
uint response_type = event->response_type & ~0x80;
|
||||
if (response_type != XCB_CLIENT_MESSAGE) {
|
||||
return false;
|
||||
}
|
||||
xcb_client_message_event_t *cm_event = reinterpret_cast<xcb_client_message_event_t *>(event);
|
||||
if (cm_event->format != 8) {
|
||||
return false;
|
||||
}
|
||||
if (cm_event->type != accept_atom1 && cm_event->type != accept_atom2) {
|
||||
return false;
|
||||
}
|
||||
char buf[21]; // can't be longer
|
||||
// Copy the data in order to null-terminate it
|
||||
qstrncpy(buf, reinterpret_cast<char *>(cm_event->data.data8), 21);
|
||||
// qDebug() << cm_event->window << "buf=\"" << buf << "\" atom=" << (cm_event->type == accept_atom1 ? "atom1" : "atom2");
|
||||
if (incoming_messages.contains(cm_event->window)) {
|
||||
if (cm_event->type == accept_atom1)
|
||||
// two different messages on the same window at the same time shouldn't happen anyway
|
||||
{
|
||||
incoming_messages[cm_event->window] = QByteArray();
|
||||
}
|
||||
incoming_messages[cm_event->window] += buf;
|
||||
} else {
|
||||
if (cm_event->type == accept_atom2) {
|
||||
return false; // middle of message, but we don't have the beginning
|
||||
}
|
||||
incoming_messages[cm_event->window] = buf;
|
||||
}
|
||||
if (strlen(buf) < 20) { // last message fragment
|
||||
Q_EMIT q->gotMessage(QString::fromUtf8(incoming_messages[cm_event->window].constData()));
|
||||
incoming_messages.remove(cm_event->window);
|
||||
}
|
||||
return false; // lets other KXMessages instances get the event too
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
send_message_internal(xcb_window_t w, const QString &msg, xcb_connection_t *c, xcb_atom_t leadingMessage, xcb_atom_t followingMessage, xcb_window_t handle);
|
||||
|
||||
KXMessages::KXMessages(const char *accept_broadcast_P, QObject *parent_P)
|
||||
: QObject(parent_P)
|
||||
, d(new KXMessagesPrivate(this,
|
||||
accept_broadcast_P,
|
||||
QX11Info::isPlatformX11() ? QX11Info::connection() : nullptr,
|
||||
QX11Info::isPlatformX11() ? QX11Info::appRootWindow() : 0))
|
||||
{
|
||||
}
|
||||
|
||||
KXMessages::KXMessages(xcb_connection_t *connection, xcb_window_t rootWindow, const char *accept_broadcast, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new KXMessagesPrivate(this, accept_broadcast, connection, rootWindow))
|
||||
{
|
||||
}
|
||||
|
||||
KXMessages::~KXMessages()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
static xcb_screen_t *defaultScreen(xcb_connection_t *c, int screen)
|
||||
{
|
||||
for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(c)); it.rem; --screen, xcb_screen_next(&it)) {
|
||||
if (screen == 0) {
|
||||
return it.data;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void KXMessages::broadcastMessage(const char *msg_type_P, const QString &message_P, int screen_P)
|
||||
{
|
||||
if (!d->valid) {
|
||||
qWarning() << "KXMessages used on non-X11 platform! This is an application bug.";
|
||||
return;
|
||||
}
|
||||
const QByteArray msg(msg_type_P);
|
||||
XcbAtom a2(d->connection, msg);
|
||||
XcbAtom a1(d->connection, msg + QByteArrayLiteral("_BEGIN"));
|
||||
xcb_window_t root = screen_P == -1 ? d->rootWindow : defaultScreen(d->connection, screen_P)->root;
|
||||
send_message_internal(root, message_P, d->connection, a1, a2, d->handle->winId());
|
||||
}
|
||||
|
||||
bool KXMessages::broadcastMessageX(xcb_connection_t *c, const char *msg_type_P, const QString &message, int screenNumber)
|
||||
{
|
||||
if (!c) {
|
||||
return false;
|
||||
}
|
||||
const QByteArray msg(msg_type_P);
|
||||
XcbAtom a2(c, msg);
|
||||
XcbAtom a1(c, msg + QByteArrayLiteral("_BEGIN"));
|
||||
const xcb_screen_t *screen = defaultScreen(c, screenNumber);
|
||||
if (!screen) {
|
||||
return false;
|
||||
}
|
||||
const xcb_window_t root = screen->root;
|
||||
const xcb_window_t win = xcb_generate_id(c);
|
||||
xcb_create_window(c, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1, 0, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT, 0, nullptr);
|
||||
send_message_internal(root, message, c, a1, a2, win);
|
||||
xcb_destroy_window(c, win);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
send_message_internal(xcb_window_t w, const QString &msg_P, xcb_connection_t *c, xcb_atom_t leadingMessage, xcb_atom_t followingMessage, xcb_window_t handle)
|
||||
{
|
||||
unsigned int pos = 0;
|
||||
QByteArray msg = msg_P.toUtf8();
|
||||
const size_t len = msg.size();
|
||||
|
||||
KXcbEvent<xcb_client_message_event_t> event;
|
||||
event.response_type = XCB_CLIENT_MESSAGE;
|
||||
event.format = 8;
|
||||
event.sequence = 0;
|
||||
event.window = handle;
|
||||
event.type = leadingMessage;
|
||||
|
||||
do {
|
||||
unsigned int i;
|
||||
for (i = 0; i < 20 && i + pos < len; ++i) {
|
||||
event.data.data8[i] = msg[i + pos];
|
||||
}
|
||||
for (; i < 20; ++i) {
|
||||
event.data.data8[i] = 0;
|
||||
}
|
||||
xcb_send_event(c, false, w, XCB_EVENT_MASK_PROPERTY_CHANGE, event.buffer());
|
||||
event.type = followingMessage;
|
||||
pos += i;
|
||||
} while (pos <= len);
|
||||
|
||||
xcb_flush(c);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#include "moc_kxmessages.cpp"
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2001-2003 Lubos Lunak <l.lunak@kde.org>
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KXMESSAGES_H
|
||||
#define KXMESSAGES_H
|
||||
|
||||
#include <QObject>
|
||||
#include <kwindowsystem_export.h>
|
||||
|
||||
#include <config-kwindowsystem.h> // KWINDOWSYSTEM_HAVE_X11
|
||||
#if KWINDOWSYSTEM_HAVE_X11
|
||||
#include <xcb/xcb.h>
|
||||
typedef struct _XDisplay Display;
|
||||
|
||||
class QString;
|
||||
|
||||
class KXMessagesPrivate;
|
||||
|
||||
/**
|
||||
* Sending string messages to other applications using the X Client Messages.
|
||||
*
|
||||
* Used internally by KStartupInfo and kstart.
|
||||
* You usually don't want to use this, use D-Bus instead.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @author Lubos Lunak <l.lunak@kde.org>
|
||||
*/
|
||||
class KWINDOWSYSTEM_EXPORT KXMessages : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Creates an instance which will receive X messages.
|
||||
*
|
||||
* @param accept_broadcast if non-nullptr, all broadcast messages with
|
||||
* this message type will be received.
|
||||
* @param parent the parent of this widget
|
||||
*/
|
||||
explicit KXMessages(const char *accept_broadcast = nullptr, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @overload
|
||||
* Overload passing in the xcb_connection_t to use instead relying on platform xcb.
|
||||
*
|
||||
* @param connection The xcb connection
|
||||
* @param rootWindow The rootWindow to use
|
||||
* @param accept_broadcast if non-nullptr, all broadcast messages with
|
||||
* this message type will be received.
|
||||
* @param parent the parent of this object
|
||||
* @since 5.8
|
||||
**/
|
||||
explicit KXMessages(xcb_connection_t *connection, xcb_window_t rootWindow, const char *accept_broadcast = nullptr, QObject *parent = nullptr);
|
||||
|
||||
~KXMessages() override;
|
||||
/**
|
||||
* Broadcasts the given message with the given message type.
|
||||
* @param msg_type the type of the message
|
||||
* @param message the message itself
|
||||
* @param screen X11 screen to use, -1 for the default
|
||||
*/
|
||||
void broadcastMessage(const char *msg_type, const QString &message, int screen = -1);
|
||||
|
||||
/**
|
||||
* Broadcasts the given message with the given message type.
|
||||
*
|
||||
* @param c X11 connection which will be used instead of
|
||||
* QX11Info::connection()
|
||||
* @param msg_type the type of the message
|
||||
* @param message the message itself
|
||||
* @param screenNumber X11 screen to use
|
||||
* @return false when an error occurred, true otherwise
|
||||
*/
|
||||
static bool broadcastMessageX(xcb_connection_t *c, const char *msg_type, const QString &message, int screenNumber);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when a message was received.
|
||||
* @param message the message that has been received
|
||||
*/
|
||||
void gotMessage(const QString &message);
|
||||
|
||||
private:
|
||||
friend class KXMessagesPrivate;
|
||||
KXMessagesPrivate *const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2008 Lubos Lunak <l.lunak@kde.org>
|
||||
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "cptr_p.h"
|
||||
#include "kxutils_p.h"
|
||||
#include <QBitmap>
|
||||
#include <QDebug>
|
||||
|
||||
#include <private/qtx11extras_p.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
namespace KXUtils
|
||||
{
|
||||
template<typename T>
|
||||
T fromNative(xcb_pixmap_t pixmap, xcb_connection_t *c)
|
||||
{
|
||||
const xcb_get_geometry_cookie_t geoCookie = xcb_get_geometry_unchecked(c, pixmap);
|
||||
UniqueCPointer<xcb_get_geometry_reply_t> geo(xcb_get_geometry_reply(c, geoCookie, nullptr));
|
||||
if (!geo) {
|
||||
// getting geometry for the pixmap failed
|
||||
return T();
|
||||
}
|
||||
|
||||
const xcb_get_image_cookie_t imageCookie = xcb_get_image_unchecked(c, XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap, 0, 0, geo->width, geo->height, ~0);
|
||||
UniqueCPointer<xcb_get_image_reply_t> xImage(xcb_get_image_reply(c, imageCookie, nullptr));
|
||||
if (!xImage) {
|
||||
// request for image data failed
|
||||
return T();
|
||||
}
|
||||
QImage::Format format = QImage::Format_Invalid;
|
||||
switch (xImage->depth) {
|
||||
case 1:
|
||||
format = QImage::Format_MonoLSB;
|
||||
break;
|
||||
case 16:
|
||||
format = QImage::Format_RGB16;
|
||||
break;
|
||||
case 24:
|
||||
format = QImage::Format_RGB32;
|
||||
break;
|
||||
case 30: {
|
||||
// Qt doesn't have a matching image format. We need to convert manually
|
||||
uint32_t *pixels = reinterpret_cast<uint32_t *>(xcb_get_image_data(xImage.get()));
|
||||
for (uint i = 0; i < xImage.get()->length; ++i) {
|
||||
int r = (pixels[i] >> 22) & 0xff;
|
||||
int g = (pixels[i] >> 12) & 0xff;
|
||||
int b = (pixels[i] >> 2) & 0xff;
|
||||
|
||||
pixels[i] = qRgba(r, g, b, 0xff);
|
||||
}
|
||||
// fall through, Qt format is still Format_ARGB32_Premultiplied
|
||||
Q_FALLTHROUGH();
|
||||
}
|
||||
case 32:
|
||||
format = QImage::Format_ARGB32_Premultiplied;
|
||||
break;
|
||||
default:
|
||||
return T(); // we don't know
|
||||
}
|
||||
QImage image(xcb_get_image_data(xImage.get()), geo->width, geo->height, xcb_get_image_data_length(xImage.get()) / geo->height, format, free, xImage.get());
|
||||
xImage.release();
|
||||
if (image.isNull()) {
|
||||
return T();
|
||||
}
|
||||
if (image.format() == QImage::Format_MonoLSB) {
|
||||
// work around an abort in QImage::color
|
||||
image.setColorCount(2);
|
||||
image.setColor(0, QColor(Qt::white).rgb());
|
||||
image.setColor(1, QColor(Qt::black).rgb());
|
||||
}
|
||||
return T::fromImage(image);
|
||||
}
|
||||
|
||||
// Create QPixmap from X pixmap. Take care of different depths if needed.
|
||||
QPixmap createPixmapFromHandle(WId pixmap, WId pixmap_mask)
|
||||
{
|
||||
return createPixmapFromHandle(QX11Info::connection(), pixmap, pixmap_mask);
|
||||
}
|
||||
|
||||
QPixmap createPixmapFromHandle(xcb_connection_t *c, WId pixmap, WId pixmap_mask)
|
||||
{
|
||||
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||
qDebug() << "Byte order not supported";
|
||||
return QPixmap();
|
||||
#endif
|
||||
const xcb_setup_t *setup = xcb_get_setup(c);
|
||||
if (setup->image_byte_order != XCB_IMAGE_ORDER_LSB_FIRST) {
|
||||
qDebug() << "Byte order not supported";
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
QPixmap pix = fromNative<QPixmap>(pixmap, c);
|
||||
if (pixmap_mask != XCB_PIXMAP_NONE) {
|
||||
QBitmap mask = fromNative<QBitmap>(pixmap_mask, c);
|
||||
if (mask.size() != pix.size()) {
|
||||
return QPixmap();
|
||||
}
|
||||
pix.setMask(mask);
|
||||
}
|
||||
return pix;
|
||||
}
|
||||
|
||||
// Functions for X timestamp comparing. For Time being 32bit they're fairly simple
|
||||
// (the #if 0 part), but on 64bit architectures Time is 64bit unsigned long,
|
||||
// so there special care needs to be taken to always use only the lower 32bits.
|
||||
#if 0
|
||||
int timestampCompare(Time time1, Time time2) // like strcmp()
|
||||
{
|
||||
if (time1 == time2) {
|
||||
return 0;
|
||||
}
|
||||
return (time1 - time2) < 0x7fffffffU ? 1 : -1; // time1 > time2 -> 1, handle wrapping
|
||||
}
|
||||
|
||||
Time timestampDiff(Time time1, Time time2) // returns time2 - time1
|
||||
{
|
||||
// no need to handle wrapping?
|
||||
return time2 - time1;
|
||||
}
|
||||
#else
|
||||
int timestampCompare(unsigned long time1_, unsigned long time2_) // like strcmp()
|
||||
{
|
||||
quint32 time1 = time1_;
|
||||
quint32 time2 = time2_;
|
||||
if (time1 == time2) {
|
||||
return 0;
|
||||
}
|
||||
return quint32(time1 - time2) < 0x7fffffffU ? 1 : -1; // time1 > time2 -> 1, handle wrapping
|
||||
}
|
||||
|
||||
int timestampDiff(unsigned long time1_, unsigned long time2_) // returns time2 - time1
|
||||
{
|
||||
// no need to handle wrapping?
|
||||
quint32 time1 = time1_;
|
||||
quint32 time2 = time2_;
|
||||
return quint32(time2 - time1);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2008 Lubos Lunak <l.lunak@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef KXUTILS_H
|
||||
#define KXUTILS_H
|
||||
|
||||
#include <QPixmap>
|
||||
#include <config-kwindowsystem.h>
|
||||
|
||||
#if KWINDOWSYSTEM_HAVE_X11
|
||||
|
||||
#include <kwindowsystem_export.h>
|
||||
|
||||
struct xcb_connection_t;
|
||||
|
||||
/**
|
||||
* Namespace with various generic X11-related functionality.
|
||||
*/
|
||||
namespace KXUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* Creates a QPixmap that contains a copy of the pixmap given by the X handle @p pixmap
|
||||
* and optionally also mask given as another X handle @mask. This function tries to
|
||||
* also handle the case when the depth of the pixmap differs from the native QPixmap depth.
|
||||
* @since 4.0.2
|
||||
*/
|
||||
QPixmap createPixmapFromHandle(WId pixmap, WId mask = 0);
|
||||
QPixmap createPixmapFromHandle(xcb_connection_t *c, WId pixmap, WId mask = 0);
|
||||
|
||||
/**
|
||||
* Compares two X timestamps, taking into account wrapping and 64bit architectures.
|
||||
* Return value is like with strcmp(), 0 for equal, -1 for time1 < time2, 1 for time1 > time2.
|
||||
* @since 4.1.0
|
||||
*/
|
||||
int timestampCompare(unsigned long time1, unsigned long time2);
|
||||
/**
|
||||
* Returns a difference of two X timestamps, time2 - time1, where time2 must be later than time1,
|
||||
* as returned by timestampCompare().
|
||||
* @since 4.1.0
|
||||
*/
|
||||
int timestampDiff(unsigned long time1, unsigned long time2);
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // KWINDOWSYSTEM_HAVE_X11
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2000 Troll Tech AS
|
||||
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef netwm_p_h
|
||||
#define netwm_p_h
|
||||
|
||||
#include <QSharedData>
|
||||
#include <QSharedDataPointer>
|
||||
|
||||
#include "atoms_p.h"
|
||||
|
||||
class Atoms : public QSharedData
|
||||
{
|
||||
public:
|
||||
explicit Atoms(xcb_connection_t *c);
|
||||
|
||||
xcb_atom_t atom(KwsAtom atom) const
|
||||
{
|
||||
return m_atoms[atom];
|
||||
}
|
||||
|
||||
private:
|
||||
void init();
|
||||
xcb_atom_t m_atoms[KwsAtomCount];
|
||||
xcb_connection_t *m_connection;
|
||||
};
|
||||
|
||||
/**
|
||||
Resizable array class.
|
||||
|
||||
This resizable array is used to simplify the implementation. The existence of
|
||||
this class is to keep the implementation from depending on a separate
|
||||
framework/library.
|
||||
@internal
|
||||
**/
|
||||
|
||||
template<class Z>
|
||||
class NETRArray
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Constructs an empty (size == 0) array.
|
||||
**/
|
||||
|
||||
NETRArray();
|
||||
|
||||
/**
|
||||
Resizable array destructor.
|
||||
**/
|
||||
|
||||
~NETRArray();
|
||||
|
||||
/**
|
||||
The [] operator does the work. If the index is larger than the current
|
||||
size of the array, it is resized.
|
||||
**/
|
||||
|
||||
Z &operator[](int);
|
||||
|
||||
/**
|
||||
Returns the size of the array.
|
||||
**/
|
||||
|
||||
int size() const
|
||||
{
|
||||
return sz;
|
||||
}
|
||||
|
||||
/**
|
||||
Resets the array (size == 0).
|
||||
**/
|
||||
void reset();
|
||||
|
||||
private:
|
||||
int sz;
|
||||
int capacity;
|
||||
Z *d;
|
||||
};
|
||||
|
||||
/**
|
||||
Private data for the NETRootInfo class.
|
||||
@internal
|
||||
**/
|
||||
|
||||
struct NETRootInfoPrivate {
|
||||
NET::Role role;
|
||||
|
||||
// information about the X server
|
||||
xcb_connection_t *conn;
|
||||
NETSize rootSize;
|
||||
xcb_window_t root;
|
||||
xcb_window_t supportwindow;
|
||||
const char *name;
|
||||
|
||||
uint32_t *temp_buf;
|
||||
size_t temp_buf_size;
|
||||
|
||||
// data that changes (either by the window manager or by a client)
|
||||
// and requires updates
|
||||
NETRArray<NETPoint> viewport;
|
||||
NETRArray<NETRect> workarea;
|
||||
NETSize geometry;
|
||||
xcb_window_t active;
|
||||
xcb_window_t *clients, *stacking, *virtual_roots;
|
||||
NETRArray<const char *> desktop_names;
|
||||
int number_of_desktops;
|
||||
int current_desktop;
|
||||
|
||||
unsigned long clients_count, stacking_count, virtual_roots_count;
|
||||
bool showing_desktop;
|
||||
NET::Orientation desktop_layout_orientation;
|
||||
NET::DesktopLayoutCorner desktop_layout_corner;
|
||||
int desktop_layout_columns, desktop_layout_rows;
|
||||
|
||||
NET::Properties properties;
|
||||
NET::Properties2 properties2;
|
||||
NET::WindowTypes windowTypes;
|
||||
NET::States states;
|
||||
NET::Actions actions;
|
||||
NET::Properties clientProperties;
|
||||
NET::Properties2 clientProperties2;
|
||||
|
||||
int ref;
|
||||
|
||||
QSharedDataPointer<Atoms> atoms;
|
||||
xcb_atom_t atom(KwsAtom atom) const
|
||||
{
|
||||
return atoms->atom(atom);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Private data for the NETWinInfo class.
|
||||
@internal
|
||||
**/
|
||||
|
||||
struct NETWinInfoPrivate {
|
||||
NET::Role role;
|
||||
|
||||
xcb_connection_t *conn;
|
||||
xcb_window_t window, root;
|
||||
NET::MappingState mapping_state;
|
||||
bool mapping_state_dirty;
|
||||
|
||||
NETRArray<NETIcon> icons;
|
||||
int icon_count;
|
||||
int *icon_sizes; // for iconSizes() only
|
||||
|
||||
NETRect icon_geom, win_geom;
|
||||
NET::States state;
|
||||
NETExtendedStrut extended_strut;
|
||||
NETStrut strut;
|
||||
NETStrut frame_strut; // strut?
|
||||
NETStrut frame_overlap;
|
||||
NETStrut gtk_frame_extents;
|
||||
NETRArray<NET::WindowType> types;
|
||||
char *name, *visible_name, *icon_name, *visible_icon_name;
|
||||
int desktop;
|
||||
int pid;
|
||||
bool handled_icons;
|
||||
xcb_timestamp_t user_time;
|
||||
char *startup_id;
|
||||
unsigned long opacity;
|
||||
xcb_window_t transient_for, window_group;
|
||||
xcb_pixmap_t icon_pixmap, icon_mask;
|
||||
NET::Actions allowed_actions;
|
||||
char *class_class, *class_name, *window_role, *client_machine, *desktop_file, *appmenu_object_path, *appmenu_service_name, *gtk_application_id;
|
||||
|
||||
NET::Properties properties;
|
||||
NET::Properties2 properties2;
|
||||
NETFullscreenMonitors fullscreen_monitors;
|
||||
bool has_net_support;
|
||||
|
||||
const char *activities;
|
||||
bool blockCompositing;
|
||||
bool urgency;
|
||||
bool input;
|
||||
NET::MappingState initialMappingState;
|
||||
NET::Protocols protocols;
|
||||
std::vector<NETRect> opaqueRegion;
|
||||
|
||||
int ref;
|
||||
|
||||
QSharedDataPointer<Atoms> atoms;
|
||||
xcb_atom_t atom(KwsAtom atom) const
|
||||
{
|
||||
return atoms->atom(atom);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // netwm_p_h
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "plugin.h"
|
||||
#include "kwindoweffects_x11.h"
|
||||
#include "kwindowshadow_p_x11.h"
|
||||
#include "kwindowsystem_p_x11.h"
|
||||
|
||||
X11Plugin::X11Plugin(QObject *parent)
|
||||
: KWindowSystemPluginInterface(parent)
|
||||
{
|
||||
}
|
||||
|
||||
X11Plugin::~X11Plugin()
|
||||
{
|
||||
}
|
||||
|
||||
KWindowEffectsPrivate *X11Plugin::createEffects()
|
||||
{
|
||||
return new KWindowEffectsPrivateX11();
|
||||
}
|
||||
|
||||
KWindowSystemPrivate *X11Plugin::createWindowSystem()
|
||||
{
|
||||
return new KWindowSystemPrivateX11();
|
||||
}
|
||||
|
||||
KWindowShadowPrivate *X11Plugin::createWindowShadow()
|
||||
{
|
||||
return new KWindowShadowPrivateX11();
|
||||
}
|
||||
|
||||
KWindowShadowTilePrivate *X11Plugin::createWindowShadowTile()
|
||||
{
|
||||
return new KWindowShadowTilePrivateX11();
|
||||
}
|
||||
|
||||
#include "moc_plugin.cpp"
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#ifndef KWINDOWSYSTEM_X11_PLUGIN_H
|
||||
#define KWINDOWSYSTEM_X11_PLUGIN_H
|
||||
|
||||
#include "kwindowsystemplugininterface_p.h"
|
||||
|
||||
class X11Plugin : public KWindowSystemPluginInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.kde.kwindowsystem.KWindowSystemPluginInterface" FILE "xcb.json")
|
||||
Q_INTERFACES(KWindowSystemPluginInterface)
|
||||
|
||||
public:
|
||||
explicit X11Plugin(QObject *parent = nullptr);
|
||||
~X11Plugin() override;
|
||||
|
||||
KWindowEffectsPrivate *createEffects() override;
|
||||
KWindowSystemPrivate *createWindowSystem() override;
|
||||
KWindowShadowPrivate *createWindowShadow() override final;
|
||||
KWindowShadowTilePrivate *createWindowShadowTile() override final;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"platforms": ["xcb"]
|
||||
}
|
||||
Reference in New Issue
Block a user