diff options
author | Ilya Fedin <fedin-ilja2010@ya.ru> | 2021-05-10 12:53:34 +0400 |
---|---|---|
committer | John Preston <johnprestonmail@gmail.com> | 2021-05-11 10:53:32 +0300 |
commit | 680a9a7ca7d9dde6c79a6340e3ca003e6ccfd8a5 (patch) | |
tree | a6743f831a1d75155a9e528a8010eaadb4d1de39 | |
parent | 7de8d6f9ac56e1148369e9f4716244f452e23d49 (diff) |
Implement parent setting for portal and gtk dialogs on Wayland via xdg-foreign-v2
10 files changed, 199 insertions, 85 deletions
diff --git a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp index ab65c36ccb..568c634065 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.cpp @@ -7,8 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/linux_gdk_helper.h" +#include "base/platform/linux/base_linux_gtk_integration.h" #include "base/platform/linux/base_linux_gtk_integration_p.h" #include "platform/linux/linux_gtk_integration_p.h" +#include "platform/linux/linux_wayland_integration.h" + +#include <QtGui/QWindow> #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION extern "C" { @@ -16,19 +20,21 @@ extern "C" { } // extern "C" #endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION +// CentOS 7 seem to be too old for needed definitions, +// so don't include until we link to gtk directly. +#if !defined DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION && defined LINK_TO_GTK +extern "C" { +#include <gdk/gdkwayland.h> +} // extern "C" +#endif // !DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION && LINK_TO_GTK + namespace Platform { namespace internal { +namespace { +using base::Platform::GtkIntegration; using namespace Platform::Gtk; -enum class GtkLoaded { - GtkNone, - Gtk2, - Gtk3, -}; - -GtkLoaded gdk_helper_loaded = GtkLoaded::GtkNone; - #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION // To be able to compile with gtk-3.0 headers as well #define GdkDrawable GdkWindow @@ -60,61 +66,91 @@ using f_gdk_x11_window_get_xid = Window(*)(GdkWindow *window); f_gdk_x11_window_get_xid gdk_x11_window_get_xid = nullptr; #endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION -bool GdkHelperLoadGtk2(QLibrary &lib) { +#ifndef DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION +using f_gdk_wayland_window_get_type = GType (*)(void); +f_gdk_wayland_window_get_type gdk_wayland_window_get_type = nullptr; + +template <typename Object> +inline bool gdk_is_wayland_window_check(Object *obj) { + return g_type_cit_helper(obj, gdk_wayland_window_get_type()); +} + +using f_gdk_wayland_window_set_transient_for_exported = gboolean(*)(GdkWindow *window, char *parent_handle_str); +f_gdk_wayland_window_set_transient_for_exported gdk_wayland_window_set_transient_for_exported = nullptr; +#endif // !DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION + +void GdkHelperLoadGtk2(QLibrary &lib) { #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION -#ifdef LINK_TO_GTK - return false; -#else // LINK_TO_GTK - if (!LOAD_GTK_SYMBOL(lib, gdk_x11_drawable_get_xdisplay)) return false; - if (!LOAD_GTK_SYMBOL(lib, gdk_x11_drawable_get_xid)) return false; - return true; -#endif // !LINK_TO_GTK -#else // !DESKTOP_APP_DISABLE_X11_INTEGRATION - return false; -#endif // DESKTOP_APP_DISABLE_X11_INTEGRATION + LOAD_GTK_SYMBOL(lib, gdk_x11_drawable_get_xdisplay); + LOAD_GTK_SYMBOL(lib, gdk_x11_drawable_get_xid); +#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION } -bool GdkHelperLoadGtk3(QLibrary &lib) { +void GdkHelperLoadGtk3(QLibrary &lib) { #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION - if (!LOAD_GTK_SYMBOL(lib, gdk_x11_window_get_type)) return false; - if (!LOAD_GTK_SYMBOL(lib, gdk_window_get_display)) return false; - if (!LOAD_GTK_SYMBOL(lib, gdk_x11_display_get_xdisplay)) return false; - if (!LOAD_GTK_SYMBOL(lib, gdk_x11_window_get_xid)) return false; - return true; -#else // !DESKTOP_APP_DISABLE_X11_INTEGRATION - return false; -#endif // DESKTOP_APP_DISABLE_X11_INTEGRATION + LOAD_GTK_SYMBOL(lib, gdk_x11_window_get_type); + LOAD_GTK_SYMBOL(lib, gdk_window_get_display); + LOAD_GTK_SYMBOL(lib, gdk_x11_display_get_xdisplay); + LOAD_GTK_SYMBOL(lib, gdk_x11_window_get_xid); +#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION +#ifndef DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION + LOAD_GTK_SYMBOL(lib, gdk_wayland_window_get_type); + LOAD_GTK_SYMBOL(lib, gdk_wayland_window_set_transient_for_exported); +#endif // !DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION } +} // namespace + void GdkHelperLoad(QLibrary &lib) { - gdk_helper_loaded = GtkLoaded::GtkNone; - if (GdkHelperLoadGtk3(lib)) { - gdk_helper_loaded = GtkLoaded::Gtk3; - } else if (GdkHelperLoadGtk2(lib)) { - gdk_helper_loaded = GtkLoaded::Gtk2; + if (const auto integration = GtkIntegration::Instance()) { + if (integration->checkVersion(3, 0, 0)) { + GdkHelperLoadGtk3(lib); + } else { + GdkHelperLoadGtk2(lib); + } } } -bool GdkHelperLoaded() { +void GdkSetTransientFor(GdkWindow *window, QWindow *parent) { +#ifndef DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION + if (gdk_wayland_window_get_type != nullptr + && gdk_wayland_window_set_transient_for_exported != nullptr + && gdk_is_wayland_window_check(window)) { + if (const auto integration = WaylandIntegration::Instance()) { + if (const auto handle = integration->nativeHandle(parent) + ; !handle.isEmpty()) { + auto handleUtf8 = handle.toUtf8(); + gdk_wayland_window_set_transient_for_exported( + window, + handleUtf8.data()); + return; + } + } + } +#endif // !DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION + #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION - return gdk_helper_loaded != GtkLoaded::GtkNone; -#else // !DESKTOP_APP_DISABLE_X11_INTEGRATION - return true; -#endif // DESKTOP_APP_DISABLE_X11_INTEGRATION -} + if (gdk_x11_window_get_type != nullptr + && gdk_x11_display_get_xdisplay != nullptr + && gdk_x11_window_get_xid != nullptr + && gdk_window_get_display != nullptr + && gdk_is_x11_window_check(window)) { + XSetTransientForHint( + gdk_x11_display_get_xdisplay(gdk_window_get_display(window)), + gdk_x11_window_get_xid(window), + parent->winId()); + return; + } +#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION -void XSetTransientForHint(GdkWindow *window, quintptr winId) { #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION - if (gdk_helper_loaded == GtkLoaded::Gtk2) { - ::XSetTransientForHint(gdk_x11_drawable_get_xdisplay(window), - gdk_x11_drawable_get_xid(window), - winId); - } else if (gdk_helper_loaded == GtkLoaded::Gtk3) { - if (gdk_is_x11_window_check(window)) { - ::XSetTransientForHint(gdk_x11_display_get_xdisplay(gdk_window_get_display(window)), - gdk_x11_window_get_xid(window), - winId); - } + if (gdk_x11_drawable_get_xdisplay != nullptr + && gdk_x11_drawable_get_xid != nullptr) { + XSetTransientForHint( + gdk_x11_drawable_get_xdisplay(window), + gdk_x11_drawable_get_xid(window), + parent->winId()); + return; } #endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION } diff --git a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.h b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.h index 6ca9536892..df06803c9e 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gdk_helper.h +++ b/Telegram/SourceFiles/platform/linux/linux_gdk_helper.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once class QLibrary; +class QWindow; extern "C" { #include <gtk/gtk.h> @@ -18,8 +19,7 @@ namespace Platform { namespace internal { void GdkHelperLoad(QLibrary &lib); -bool GdkHelperLoaded(); -void XSetTransientForHint(GdkWindow *window, quintptr winId); +void GdkSetTransientFor(GdkWindow *window, QWindow *parent); } // namespace internal } // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp index 9befbf8973..1c00393655 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_file_dialog.cpp @@ -70,8 +70,7 @@ QStringList cleanFilterList(const QString &filter) { } bool Supported() { - return internal::GdkHelperLoaded() - && (gtk_widget_hide_on_delete != nullptr) + return (gtk_widget_hide_on_delete != nullptr) && (gtk_clipboard_store != nullptr) && (gtk_clipboard_get != nullptr) && (gtk_widget_destroy != nullptr) @@ -273,7 +272,7 @@ void QGtkDialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindo gtk_widget_realize(gtkWidget); // creates X window if (parent) { - internal::XSetTransientForHint(gtk_widget_get_window(gtkWidget), parent->winId()); + internal::GdkSetTransientFor(gtk_widget_get_window(gtkWidget), parent); } if (modality != Qt::NonModal) { diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_open_with_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_open_with_dialog.cpp index 1c8710bae5..4994ebe7dc 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_open_with_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_open_with_dialog.cpp @@ -22,8 +22,7 @@ namespace { using namespace Platform::Gtk; bool Supported() { - return Platform::internal::GdkHelperLoaded() - && (gtk_app_chooser_dialog_new != nullptr) + return (gtk_app_chooser_dialog_new != nullptr) && (gtk_app_chooser_get_app_info != nullptr) && (gtk_app_chooser_get_type != nullptr) && (gtk_widget_get_window != nullptr) @@ -70,9 +69,9 @@ bool GtkOpenWithDialog::exec() { gtk_widget_realize(_gtkWidget); if (const auto activeWindow = Core::App().activeWindow()) { - Platform::internal::XSetTransientForHint( + Platform::internal::GdkSetTransientFor( gtk_widget_get_window(_gtkWidget), - activeWindow->widget().get()->windowHandle()->winId()); + activeWindow->widget()->windowHandle()); } QGuiApplicationPrivate::showModalWindow(this); diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp index a3ff427615..6586988d89 100644 --- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp @@ -9,8 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" +#include <QtCore/QCoreApplication> #include <connection_thread.h> #include <registry.h> +#include <surface.h> +#include <xdgforeign.h> using namespace KWayland::Client; @@ -25,6 +28,10 @@ public: return _registry; } + [[nodiscard]] XdgExporter *xdgExporter() { + return _xdgExporter.get(); + } + [[nodiscard]] QEventLoop &interfacesLoop() { return _interfacesLoop; } @@ -35,12 +42,25 @@ public: private: ConnectionThread _connection; + ConnectionThread *_applicationConnection = nullptr; Registry _registry; + Registry _applicationRegistry; + std::unique_ptr<XdgExporter> _xdgExporter; QEventLoop _interfacesLoop; bool _interfacesAnnounced = false; }; -WaylandIntegration::Private::Private() { +WaylandIntegration::Private::Private() +: _applicationConnection(ConnectionThread::fromApplication(this)) { + _applicationRegistry.create(_applicationConnection); + _applicationRegistry.setup(); + + connect( + _applicationConnection, + &ConnectionThread::connectionDied, + &_applicationRegistry, + &Registry::destroy); + connect(&_connection, &ConnectionThread::connected, [=] { LOG(("Successfully connected to Wayland server at socket: %1") .arg(_connection.socketName())); @@ -62,6 +82,22 @@ WaylandIntegration::Private::Private() { } }); + connect( + &_applicationRegistry, + &Registry::exporterUnstableV2Announced, + [=](uint name, uint version) { + _xdgExporter = { + _applicationRegistry.createXdgExporter(name, version), + std::default_delete<XdgExporter>(), + }; + + connect( + QCoreApplication::instance(), + &QCoreApplication::aboutToQuit, + this, + [=] { _xdgExporter = nullptr; }); + }); + _connection.initConnection(); } @@ -89,5 +125,25 @@ bool WaylandIntegration::supportsXdgDecoration() { Registry::Interface::XdgDecorationUnstableV1); } +QString WaylandIntegration::nativeHandle(QWindow *window) { + if (const auto exporter = _private->xdgExporter()) { + if (const auto surface = Surface::fromWindow(window)) { + if (const auto exported = exporter->exportTopLevel( + surface, + _private->xdgExporter())) { + QEventLoop loop; + QObject::connect( + exported, + &XdgExported::done, + &loop, + &QEventLoop::quit); + loop.exec(); + return exported->handle(); + } + } + } + return {}; +} + } // namespace internal } // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h index b2f7161846..a41d342098 100644 --- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h +++ b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h @@ -15,6 +15,7 @@ public: static WaylandIntegration *Instance(); void waitForInterfaceAnnounce(); bool supportsXdgDecoration(); + QString nativeHandle(QWindow *window); private: WaylandIntegration(); diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp b/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp index 166e8b575d..2b4068553c 100644 --- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp @@ -33,5 +33,9 @@ bool WaylandIntegration::supportsXdgDecoration() { return false; } +QString WaylandIntegration::nativeHandle(QWindow *window) { + return {}; +} + } // namespace internal } // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp index c9476a0c50..543908f24a 100644 --- a/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_xdp_file_dialog.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_file_utilities.h" #include "base/platform/base_platform_info.h" #include "base/platform/linux/base_linux_glibmm_helper.h" +#include "platform/linux/linux_wayland_integration.h" #include "storage/localstorage.h" #include "base/openssl_help.h" #include "base/qt_adapters.h" @@ -20,6 +21,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include <glibmm.h> #include <giomm.h> +using Platform::internal::WaylandIntegration; + namespace Platform { namespace FileDialog { namespace XDP { @@ -207,7 +210,7 @@ private: uint _requestSignalId = 0; // Options - WId _winId = 0; + QWindow *_parent = nullptr; QFileDialog::Options _options; QFileDialog::AcceptMode _acceptMode = QFileDialog::AcceptOpen; QFileDialog::FileMode _fileMode = QFileDialog::ExistingFile; @@ -266,8 +269,13 @@ XDPFileDialog::~XDPFileDialog() { void XDPFileDialog::openPortal() { std::stringstream parentWindowId; - if (IsX11()) { - parentWindowId << "x11:" << std::hex << _winId; + if (const auto integration = WaylandIntegration::Instance()) { + if (const auto handle = integration->nativeHandle(_parent) + ; !handle.isEmpty()) { + parentWindowId << "wayland:" << handle.toStdString(); + } + } else if (IsX11() && _parent) { + parentWindowId << "x11:" << std::hex << _parent->winId(); } std::map<Glib::ustring, Glib::VariantBase> options; @@ -408,7 +416,7 @@ void XDPFileDialog::openPortal() { + uniqueName + '/' + handleToken; - + const auto responseCallback = crl::guard(this, [=]( const Glib::RefPtr<Gio::DBus::Connection> &connection, const Glib::ustring &sender_name, @@ -629,7 +637,7 @@ void XDPFileDialog::showHelper( Qt::WindowModality windowModality, QWindow *parent) { _modal = windowModality != Qt::NonModal; - _winId = parent ? parent->winId() : 0; + _parent = parent; openPortal(); } diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp index 64d3db95dd..810bfeb21d 100644 --- a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/base_platform_info.h" #include "base/platform/linux/base_linux_glibmm_helper.h" +#include "platform/linux/linux_wayland_integration.h" #include "core/application.h" #include "window/window_controller.h" #include "base/openssl_help.h" @@ -21,6 +22,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include <giomm.h> #include <private/qguiapplication_p.h> +using Platform::internal::WaylandIntegration; + namespace Platform { namespace File { namespace internal { @@ -80,18 +83,22 @@ bool XDPOpenWithDialog::exec() { const auto parentWindowId = [&]() -> Glib::ustring { std::stringstream result; - if (const auto activeWindow = Core::App().activeWindow()) { - if (IsX11()) { - result - << "x11:" - << std::hex - << activeWindow - ->widget() - .get() - ->windowHandle() - ->winId(); + + const auto activeWindow = Core::App().activeWindow(); + if (!activeWindow) { + return result.str(); + } + + const auto window = activeWindow->widget()->windowHandle(); + if (const auto integration = WaylandIntegration::Instance()) { + if (const auto handle = integration->nativeHandle(window) + ; !handle.isEmpty()) { + result << "wayland:" << handle.toStdString(); } + } else if (IsX11()) { + result << "x11:" << std::hex << window->winId(); } + return result.str(); }(); diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 890a7c0c4a..971bf3a50f 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -102,18 +102,22 @@ PortalAutostart::PortalAutostart(bool start, bool silent) { const auto parentWindowId = [&]() -> Glib::ustring { std::stringstream result; - if (const auto activeWindow = Core::App().activeWindow()) { - if (IsX11()) { - result - << "x11:" - << std::hex - << activeWindow - ->widget() - .get() - ->windowHandle() - ->winId(); + + const auto activeWindow = Core::App().activeWindow(); + if (!activeWindow) { + return result.str(); + } + + const auto window = activeWindow->widget()->windowHandle(); + if (const auto integration = WaylandIntegration::Instance()) { + if (const auto handle = integration->nativeHandle(window) + ; !handle.isEmpty()) { + result << "wayland:" << handle.toStdString(); } + } else if (IsX11()) { + result << "x11:" << std::hex << window->winId(); } + return result.str(); }(); |