diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 085c42eb6..5bd0a27e1 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -815,14 +815,16 @@ PRIVATE platform/linux/linux_gtk_integration_p.h platform/linux/linux_gtk_integration.cpp platform/linux/linux_gtk_integration.h + platform/linux/linux_gtk_open_with_dialog.cpp + platform/linux/linux_gtk_open_with_dialog.h platform/linux/linux_notification_service_watcher.cpp platform/linux/linux_notification_service_watcher.h - platform/linux/linux_open_with_dialog.cpp - platform/linux/linux_open_with_dialog.h platform/linux/linux_wayland_integration.cpp platform/linux/linux_wayland_integration.h platform/linux/linux_xdp_file_dialog.cpp platform/linux/linux_xdp_file_dialog.h + platform/linux/linux_xdp_open_with_dialog.cpp + platform/linux/linux_xdp_open_with_dialog.h platform/linux/file_utilities_linux.cpp platform/linux/file_utilities_linux.h platform/linux/launcher_linux.cpp @@ -1111,6 +1113,8 @@ if (DESKTOP_APP_DISABLE_DBUS_INTEGRATION) platform/linux/linux_notification_service_watcher.h platform/linux/linux_xdp_file_dialog.cpp platform/linux/linux_xdp_file_dialog.h + platform/linux/linux_xdp_open_with_dialog.cpp + platform/linux/linux_xdp_open_with_dialog.h platform/linux/notifications_manager_linux.cpp ) @@ -1133,8 +1137,8 @@ if (DESKTOP_APP_DISABLE_GTK_INTEGRATION) platform/linux/linux_gtk_file_dialog.h platform/linux/linux_gtk_integration_p.h platform/linux/linux_gtk_integration.cpp - platform/linux/linux_open_with_dialog.cpp - platform/linux/linux_open_with_dialog.h + platform/linux/linux_gtk_open_with_dialog.cpp + platform/linux/linux_gtk_open_with_dialog.h ) nice_target_sources(Telegram ${src_loc} diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp index 2e34a1e19..d1928d471 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION #include "platform/linux/linux_xdp_file_dialog.h" +#include "platform/linux/linux_xdp_open_with_dialog.h" #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION #include @@ -41,6 +42,12 @@ void UnsafeOpenEmailLink(const QString &email) { } bool UnsafeShowOpenWith(const QString &filepath) { +#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION + if (internal::ShowXDPOpenWithDialog(filepath)) { + return true; + } +#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION + if (InFlatpak() || InSnap()) { return false; } diff --git a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp index b9f36c5fb..2762db0a8 100644 --- a/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_integration.cpp @@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/linux/linux_gtk_integration_p.h" #include "platform/linux/linux_gdk_helper.h" #include "platform/linux/linux_gtk_file_dialog.h" -#include "platform/linux/linux_open_with_dialog.h" +#include "platform/linux/linux_gtk_open_with_dialog.h" namespace Platform { namespace internal { @@ -185,7 +185,7 @@ bool GtkIntegration::getFileDialog( } bool GtkIntegration::showOpenWithDialog(const QString &filepath) const { - return File::internal::ShowOpenWithDialog(filepath); + return File::internal::ShowGtkOpenWithDialog(filepath); } QImage GtkIntegration::getImageFromClipboard() const { diff --git a/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_gtk_open_with_dialog.cpp similarity index 80% rename from Telegram/SourceFiles/platform/linux/linux_open_with_dialog.cpp rename to Telegram/SourceFiles/platform/linux/linux_gtk_open_with_dialog.cpp index 5e87f10e8..1c8710bae 100644 --- a/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_open_with_dialog.cpp @@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ -#include "platform/linux/linux_open_with_dialog.h" +#include "platform/linux/linux_gtk_open_with_dialog.h" #include "platform/linux/linux_gtk_integration_p.h" #include "platform/linux/linux_gdk_helper.h" @@ -21,7 +21,7 @@ namespace { using namespace Platform::Gtk; -bool OpenWithDialogSupported() { +bool Supported() { return Platform::internal::GdkHelperLoaded() && (gtk_app_chooser_dialog_new != nullptr) && (gtk_app_chooser_get_app_info != nullptr) @@ -32,15 +32,15 @@ bool OpenWithDialogSupported() { && (gtk_widget_destroy != nullptr); } -class OpenWithDialog : public QWindow { +class GtkOpenWithDialog : public QWindow { public: - OpenWithDialog(const QString &filepath); - ~OpenWithDialog(); + GtkOpenWithDialog(const QString &filepath); + ~GtkOpenWithDialog(); bool exec(); private: - static void handleResponse(OpenWithDialog *dialog, int responseId); + static void handleResponse(GtkOpenWithDialog *dialog, int responseId); GFile *_gfileInstance = nullptr; GtkWidget *_gtkWidget = nullptr; @@ -48,7 +48,7 @@ private: std::optional _result; }; -OpenWithDialog::OpenWithDialog(const QString &filepath) +GtkOpenWithDialog::GtkOpenWithDialog(const QString &filepath) : _gfileInstance(g_file_new_for_path(filepath.toUtf8().constData())) , _gtkWidget(gtk_app_chooser_dialog_new( nullptr, @@ -61,12 +61,12 @@ OpenWithDialog::OpenWithDialog(const QString &filepath) this); } -OpenWithDialog::~OpenWithDialog() { +GtkOpenWithDialog::~GtkOpenWithDialog() { gtk_widget_destroy(_gtkWidget); g_object_unref(_gfileInstance); } -bool OpenWithDialog::exec() { +bool GtkOpenWithDialog::exec() { gtk_widget_realize(_gtkWidget); if (const auto activeWindow = Core::App().activeWindow()) { @@ -86,7 +86,7 @@ bool OpenWithDialog::exec() { return *_result; } -void OpenWithDialog::handleResponse(OpenWithDialog *dialog, int responseId) { +void GtkOpenWithDialog::handleResponse(GtkOpenWithDialog *dialog, int responseId) { GAppInfo *chosenAppInfo = nullptr; dialog->_result = true; @@ -119,12 +119,12 @@ void OpenWithDialog::handleResponse(OpenWithDialog *dialog, int responseId) { } // namespace -bool ShowOpenWithDialog(const QString &filepath) { - if (!OpenWithDialogSupported()) { +bool ShowGtkOpenWithDialog(const QString &filepath) { + if (!Supported()) { return false; } - return OpenWithDialog(filepath).exec(); + return GtkOpenWithDialog(filepath).exec(); } } // namespace internal diff --git a/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.h b/Telegram/SourceFiles/platform/linux/linux_gtk_open_with_dialog.h similarity index 87% rename from Telegram/SourceFiles/platform/linux/linux_open_with_dialog.h rename to Telegram/SourceFiles/platform/linux/linux_gtk_open_with_dialog.h index 3a29f6b09..592345a1a 100644 --- a/Telegram/SourceFiles/platform/linux/linux_open_with_dialog.h +++ b/Telegram/SourceFiles/platform/linux/linux_gtk_open_with_dialog.h @@ -11,7 +11,7 @@ namespace Platform { namespace File { namespace internal { -bool ShowOpenWithDialog(const QString &filepath); +bool ShowGtkOpenWithDialog(const QString &filepath); } // namespace internal } // namespace File diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp new file mode 100644 index 000000000..64d3db95d --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp @@ -0,0 +1,180 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "platform/linux/linux_xdp_open_with_dialog.h" + +#include "base/platform/base_platform_info.h" +#include "base/platform/linux/base_linux_glibmm_helper.h" +#include "core/application.h" +#include "window/window_controller.h" +#include "base/openssl_help.h" + +#include + +#include +#include +#include +#include +#include + +namespace Platform { +namespace File { +namespace internal { +namespace { + +constexpr auto kXDGDesktopPortalService = "org.freedesktop.portal.Desktop"_cs; +constexpr auto kXDGDesktopPortalObjectPath = "/org/freedesktop/portal/desktop"_cs; +constexpr auto kXDGDesktopPortalOpenURIInterface = "org.freedesktop.portal.OpenURI"_cs; +constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs; + +class XDPOpenWithDialog : public QWindow { +public: + XDPOpenWithDialog(const QString &filepath) + : _filepath(filepath.toStdString()) { + } + + bool exec(); + +private: + Glib::ustring _filepath; +}; + +bool XDPOpenWithDialog::exec() { + try { + const auto connection = Gio::DBus::Connection::get_sync( + Gio::DBus::BusType::BUS_TYPE_SESSION); + + auto reply = connection->call_sync( + std::string(kXDGDesktopPortalObjectPath), + std::string(kPropertiesInterface), + "Get", + base::Platform::MakeGlibVariant(std::tuple{ + Glib::ustring( + std::string(kXDGDesktopPortalOpenURIInterface)), + Glib::ustring("version"), + }), + std::string(kXDGDesktopPortalService)); + + const auto version = base::Platform::GlibVariantCast( + base::Platform::GlibVariantCast( + reply.get_child(0))); + + if (version < 3) { + return false; + } + + const auto fd = open( + _filepath.c_str(), + O_RDONLY); + + if (fd == -1) { + return false; + } + + const auto fdGuard = gsl::finally([&] { ::close(fd); }); + auto outFdList = Glib::RefPtr(); + + 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(); + } + } + return result.str(); + }(); + + const auto handleToken = Glib::ustring("tdesktop") + + std::to_string(openssl::RandomValue()); + + auto uniqueName = connection->get_unique_name(); + uniqueName.erase(0, 1); + uniqueName.replace(uniqueName.find('.'), 1, 1, '_'); + + const auto requestPath = Glib::ustring( + "/org/freedesktop/portal/desktop/request/") + + uniqueName + + '/' + + handleToken; + + QEventLoop loop; + + const auto signalId = connection->signal_subscribe( + [&]( + const Glib::RefPtr &connection, + const Glib::ustring &sender_name, + const Glib::ustring &object_path, + const Glib::ustring &interface_name, + const Glib::ustring &signal_name, + const Glib::VariantContainerBase ¶meters) { + loop.quit(); + }, + std::string(kXDGDesktopPortalService), + "org.freedesktop.portal.Request", + "Response", + requestPath); + + const auto signalGuard = gsl::finally([&] { + if (signalId != 0) { + connection->signal_unsubscribe(signalId); + } + }); + + connection->call_sync( + std::string(kXDGDesktopPortalObjectPath), + std::string(kXDGDesktopPortalOpenURIInterface), + "OpenFile", + Glib::VariantContainerBase::create_tuple({ + Glib::Variant::create(parentWindowId), + Glib::wrap(g_variant_new_handle(0)), + Glib::Variant>::create({ + { + "handle_token", + Glib::Variant::create(handleToken) + }, + { + "ask", + Glib::Variant::create(true) + }, + }), + }), + Glib::wrap(g_unix_fd_list_new_from_array(&fd, 1)), + outFdList, + std::string(kXDGDesktopPortalService)); + + if (signalId != 0) { + QGuiApplicationPrivate::showModalWindow(this); + loop.exec(); + QGuiApplicationPrivate::hideModalWindow(this); + } + + return true; + } catch (...) { + } + + return false; +} + +} // namespace + +bool ShowXDPOpenWithDialog(const QString &filepath) { + return XDPOpenWithDialog(filepath).exec(); +} + +} // namespace internal +} // namespace File +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.h b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.h new file mode 100644 index 000000000..a848baaf5 --- /dev/null +++ b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.h @@ -0,0 +1,18 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Platform { +namespace File { +namespace internal { + +bool ShowXDPOpenWithDialog(const QString &filepath); + +} // namespace internal +} // namespace File +} // namespace Platform