Add portal-based open with dialog implementation

This commit is contained in:
Ilya Fedin 2021-03-07 02:29:13 +04:00 committed by John Preston
parent a27a54798c
commit b918170464
7 changed files with 229 additions and 20 deletions

View file

@ -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}

View file

@ -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 <QtGui/QDesktopServices>
@ -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;
}

View file

@ -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 {

View file

@ -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<bool> _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

View file

@ -11,7 +11,7 @@ namespace Platform {
namespace File {
namespace internal {
bool ShowOpenWithDialog(const QString &filepath);
bool ShowGtkOpenWithDialog(const QString &filepath);
} // namespace internal
} // namespace File

View file

@ -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 <QtGui/QWindow>
#include <fcntl.h>
#include <gio/gunixfdlist.h>
#include <glibmm.h>
#include <giomm.h>
#include <private/qguiapplication_p.h>
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<uint>(
base::Platform::GlibVariantCast<Glib::VariantBase>(
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<Gio::UnixFDList>();
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<uint>());
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<Gio::DBus::Connection> &connection,
const Glib::ustring &sender_name,
const Glib::ustring &object_path,
const Glib::ustring &interface_name,
const Glib::ustring &signal_name,
const Glib::VariantContainerBase &parameters) {
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<Glib::ustring>::create(parentWindowId),
Glib::wrap(g_variant_new_handle(0)),
Glib::Variant<std::map<
Glib::ustring,
Glib::VariantBase
>>::create({
{
"handle_token",
Glib::Variant<Glib::ustring>::create(handleToken)
},
{
"ask",
Glib::Variant<bool>::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

View file

@ -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