From 6ee08faa2410626b324a53b094c3bb8458347755 Mon Sep 17 00:00:00 2001
From: Ilya Fedin <fedin-ilja2010@ya.ru>
Date: Fri, 3 Jun 2022 20:09:17 +0400
Subject: [PATCH] Migrate from kwayland to QtWaylandScanner

---
 .gitmodules                                   |   6 -
 Telegram/CMakeLists.txt                       |  13 +-
 .../linux/linux_wayland_integration.cpp       | 199 ++++++++++++++----
 Telegram/ThirdParty/extra-cmake-modules       |   1 -
 Telegram/ThirdParty/kwayland                  |   1 -
 5 files changed, 165 insertions(+), 55 deletions(-)
 delete mode 160000 Telegram/ThirdParty/extra-cmake-modules
 delete mode 160000 Telegram/ThirdParty/kwayland

diff --git a/.gitmodules b/.gitmodules
index af8be44b6..1d4d04f21 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -85,15 +85,9 @@
 [submodule "Telegram/ThirdParty/jemalloc"]
 	path = Telegram/ThirdParty/jemalloc
 	url = https://github.com/jemalloc/jemalloc
-[submodule "Telegram/ThirdParty/kwayland"]
-	path = Telegram/ThirdParty/kwayland
-	url = https://github.com/KDE/kwayland.git
 [submodule "Telegram/ThirdParty/dispatch"]
 	path = Telegram/ThirdParty/dispatch
 	url = https://github.com/apple/swift-corelibs-libdispatch
-[submodule "Telegram/ThirdParty/extra-cmake-modules"]
-	path = Telegram/ThirdParty/extra-cmake-modules
-	url = https://github.com/KDE/extra-cmake-modules.git
 [submodule "Telegram/ThirdParty/plasma-wayland-protocols"]
 	path = Telegram/ThirdParty/plasma-wayland-protocols
 	url = https://github.com/KDE/plasma-wayland-protocols.git
diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index 7052bd3fa..1dadeea04 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -95,7 +95,7 @@ PRIVATE
     desktop-app::external_xxhash
 )
 
-target_precompile_headers(Telegram PRIVATE ${src_loc}/stdafx.h)
+target_precompile_headers(Telegram PRIVATE $<$<COMPILE_LANGUAGE:CXX,OBJCXX>:${src_loc}/stdafx.h>)
 nice_target_sources(Telegram ${src_loc}
 PRIVATE
     ${style_files}
@@ -1436,10 +1436,19 @@ else()
     endif()
 
     if (NOT DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION)
+        if (DESKTOP_APP_QT6)
+            qt6_generate_wayland_protocol_client_sources(Telegram
+            FILES
+                ${third_party_loc}/plasma-wayland-protocols/src/protocols/plasma-shell.xml
+            )
+        else()
+            message(FATAL_ERROR "This piece of cmake code is not ported to Qt 5")
+        endif()
+
         target_link_libraries(Telegram
         PRIVATE
             desktop-app::lib_waylandshells
-            desktop-app::external_kwayland
+            desktop-app::external_wayland_client
         )
     endif()
 endif()
diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp
index 7641579aa..ea3d5f0cd 100644
--- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp
+++ b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp
@@ -8,52 +8,171 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "platform/linux/linux_wayland_integration.h"
 
 #include "base/platform/base_platform_info.h"
+#include "base/qt_signal_producer.h"
+#include "base/flat_map.h"
+#include "qwayland-plasma-shell.h"
 
-#include <connection_thread.h>
-#include <registry.h>
-#include <surface.h>
-#include <plasmashell.h>
-
-using namespace KWayland::Client;
+#include <QtGui/QGuiApplication>
+#include <QtGui/QWindow>
+#include <qpa/qplatformnativeinterface.h>
+#include <wayland-client.h>
 
 namespace Platform {
 namespace internal {
+namespace {
+
+struct WlRegistryDeleter {
+	void operator()(wl_registry *value) {
+		wl_registry_destroy(value);
+	}
+};
+
+struct PlasmaSurfaceDeleter {
+	void operator()(org_kde_plasma_surface *value) {
+		org_kde_plasma_surface_destroy(value);
+	}
+};
+
+template <typename T>
+class QtWaylandAutoDestroyer : public T {
+public:
+	QtWaylandAutoDestroyer() = default;
+
+	~QtWaylandAutoDestroyer() {
+		if (!this->isInitialized()) {
+			return;
+		}
+
+		static constexpr auto HasDestroy = requires(const T &t) {
+			t.destroy();
+		};
+
+		if constexpr (HasDestroy) {
+			this->destroy();
+		} else {
+			free(this->object());
+			this->init(nullptr);
+		}
+	}
+};
+
+} // namespace
 
 struct WaylandIntegration::Private {
-	std::unique_ptr<ConnectionThread> connection;
-	Registry registry;
-	std::unique_ptr<PlasmaShell> plasmaShell;
+	org_kde_plasma_surface *plasmaSurface(QWindow *window);
+	std::unique_ptr<wl_registry, WlRegistryDeleter> registry;
+	QtWaylandAutoDestroyer<QtWayland::org_kde_plasma_shell> plasmaShell;
+	uint32_t plasmaShellName = 0;
+	base::flat_map<wl_surface*, std::unique_ptr<
+		org_kde_plasma_surface,
+		PlasmaSurfaceDeleter>> plasmaSurfaces;
+	rpl::lifetime lifetime;
+
+	static const struct wl_registry_listener RegistryListener;
 };
 
+const struct wl_registry_listener WaylandIntegration::Private::RegistryListener = {
+	decltype(wl_registry_listener::global)(+[](
+			Private *data,
+			wl_registry *registry,
+			uint32_t name,
+			const char *interface,
+			uint32_t version) {
+		if (interface == qstr("org_kde_plasma_shell")) {
+			data->plasmaShell.init(registry, name, version);
+			data->plasmaShellName = name;
+		}
+	}),
+	decltype(wl_registry_listener::global_remove)(+[](
+			Private *data,
+			wl_registry *registry,
+			uint32_t name) {
+		if (name == data->plasmaShellName) {
+			free(data->plasmaShell.object());
+			data->plasmaShell.init(nullptr);
+			data->plasmaShellName = 0;
+		}
+	}),
+};
+
+org_kde_plasma_surface *WaylandIntegration::Private::plasmaSurface(
+		QWindow *window) {
+	if (!plasmaShell.isInitialized()) {
+		return nullptr;
+	}
+
+	const auto native = QGuiApplication::platformNativeInterface();
+	if (!native) {
+		return nullptr;
+	}
+
+	const auto surface = reinterpret_cast<wl_surface*>(
+		native->nativeResourceForWindow(QByteArray("surface"), window));
+
+	if (!surface) {
+		return nullptr;
+	}
+
+	const auto it = plasmaSurfaces.find(surface);
+	if (it != plasmaSurfaces.cend()) {
+		return it->second.get();
+	}
+
+	const auto result = plasmaShell.get_surface(surface);
+	if (!result) {
+		return nullptr;
+	}
+
+	plasmaSurfaces.emplace(surface, result);
+
+	base::qt_signal_producer(
+		window,
+		&QObject::destroyed
+	) | rpl::start_with_next([=] {
+		auto it = plasmaSurfaces.find(surface);
+		if (it != plasmaSurfaces.cend()) {
+			plasmaSurfaces.erase(it);
+		}
+	}, lifetime);
+
+	return result;
+}
+
 WaylandIntegration::WaylandIntegration()
 : _private(std::make_unique<Private>()) {
-	_private->connection = std::unique_ptr<ConnectionThread>{
-		ConnectionThread::fromApplication(),
-	};
+	const auto native = QGuiApplication::platformNativeInterface();
+	if (!native) {
+		return;
+	}
 
-	_private->registry.create(_private->connection.get());
-	_private->registry.setup();
+	const auto display = reinterpret_cast<wl_display*>(
+		native->nativeResourceForIntegration(QByteArray("wl_display")));
 
-	QObject::connect(
-		_private->connection.get(),
-		&ConnectionThread::connectionDied,
-		&_private->registry,
-		&Registry::destroy);
+	if (!display) {
+		return;
+	}
 
-	QObject::connect(
-		&_private->registry,
-		&Registry::plasmaShellAnnounced,
-		[=](uint name, uint version) {
-			_private->plasmaShell = std::unique_ptr<PlasmaShell>{
-				_private->registry.createPlasmaShell(name, version),
-			};
+	_private->registry.reset(wl_display_get_registry(display));
+	wl_registry_add_listener(
+		_private->registry.get(),
+		&Private::RegistryListener,
+		_private.get());
 
-			QObject::connect(
-				_private->connection.get(),
-				&ConnectionThread::connectionDied,
-				_private->plasmaShell.get(),
-				&PlasmaShell::destroy);
-		});
+	base::qt_signal_producer(
+		native,
+		&QObject::destroyed
+	) | rpl::start_with_next([=] {
+		// too late for standard destructors, just free
+		for (auto it = _private->plasmaSurfaces.begin()
+			; it != _private->plasmaSurfaces.cend()
+			; ++it) {
+			free(it->second.release());
+			_private->plasmaSurfaces.erase(it);
+		}
+		free(_private->plasmaShell.object());
+		_private->plasmaShell.init(nullptr);
+		free(_private->registry.release());
+	}, _private->lifetime);
 }
 
 WaylandIntegration::~WaylandIntegration() = default;
@@ -65,26 +184,16 @@ WaylandIntegration *WaylandIntegration::Instance() {
 }
 
 bool WaylandIntegration::skipTaskbarSupported() {
-	return _private->plasmaShell != nullptr;
+	return _private->plasmaShell.isInitialized();
 }
 
 void WaylandIntegration::skipTaskbar(QWindow *window, bool skip) {
-	const auto shell = _private->plasmaShell.get();
-	if (!shell) {
-		return;
-	}
-
-	const auto surface = Surface::fromWindow(window);
-	if (!surface) {
-		return;
-	}
-
-	const auto plasmaSurface = shell->createSurface(surface, surface);
+	auto plasmaSurface = _private->plasmaSurface(window);
 	if (!plasmaSurface) {
 		return;
 	}
 
-	plasmaSurface->setSkipTaskbar(skip);
+	org_kde_plasma_surface_set_skip_taskbar(plasmaSurface, skip);
 }
 
 } // namespace internal
diff --git a/Telegram/ThirdParty/extra-cmake-modules b/Telegram/ThirdParty/extra-cmake-modules
deleted file mode 160000
index eccf69582..000000000
--- a/Telegram/ThirdParty/extra-cmake-modules
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit eccf695824fa931bca471bad3f1aa3a1f244fdd6
diff --git a/Telegram/ThirdParty/kwayland b/Telegram/ThirdParty/kwayland
deleted file mode 160000
index 15af039af..000000000
--- a/Telegram/ThirdParty/kwayland
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 15af039af2044c7a2d7cfea0d2c1a99bf6c151ac