Move GTK integration out of process with D-Bus
This commit is contained in:
parent
d3c9bb0bc6
commit
551ea7d879
|
@ -93,6 +93,9 @@ jobs:
|
||||||
DEFINE=""
|
DEFINE=""
|
||||||
if [ -n "${{ matrix.defines }}" ]; then
|
if [ -n "${{ matrix.defines }}" ]; then
|
||||||
DEFINE="-D ${{ matrix.defines }}=ON"
|
DEFINE="-D ${{ matrix.defines }}=ON"
|
||||||
|
if [ "${{ matrix.defines }}" == "DESKTOP_APP_DISABLE_DBUS_INTEGRATION" ]; then
|
||||||
|
DEFINE="$DEFINE -D DESKTOP_APP_DISABLE_GTK_INTEGRATION=ON"
|
||||||
|
fi
|
||||||
echo Define from matrix: $DEFINE
|
echo Define from matrix: $DEFINE
|
||||||
echo "ARTIFACT_NAME=Telegram_${{ matrix.defines }}" >> $GITHUB_ENV
|
echo "ARTIFACT_NAME=Telegram_${{ matrix.defines }}" >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
|
|
|
@ -112,6 +112,7 @@ if (LINUX)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT DESKTOP_APP_DISABLE_GTK_INTEGRATION)
|
if (NOT DESKTOP_APP_DISABLE_GTK_INTEGRATION)
|
||||||
|
target_link_libraries(Telegram PRIVATE rt)
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
if (DESKTOP_APP_USE_PACKAGED AND NOT DESKTOP_APP_USE_PACKAGED_LAZY)
|
if (DESKTOP_APP_USE_PACKAGED AND NOT DESKTOP_APP_USE_PACKAGED_LAZY)
|
||||||
|
|
|
@ -19,7 +19,7 @@ public:
|
||||||
|
|
||||||
static std::unique_ptr<Launcher> Create(int argc, char *argv[]);
|
static std::unique_ptr<Launcher> Create(int argc, char *argv[]);
|
||||||
|
|
||||||
int exec();
|
virtual int exec();
|
||||||
|
|
||||||
QString argumentsString() const;
|
QString argumentsString() const;
|
||||||
bool customWorkingDir() const;
|
bool customWorkingDir() const;
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "core/crash_reports.h"
|
#include "core/crash_reports.h"
|
||||||
#include "core/update_checker.h"
|
#include "core/update_checker.h"
|
||||||
|
#include "platform/linux/linux_gtk_integration.h"
|
||||||
|
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
|
|
||||||
|
@ -23,6 +24,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using Platform::internal::GtkIntegration;
|
||||||
|
|
||||||
class Arguments {
|
class Arguments {
|
||||||
public:
|
public:
|
||||||
void push(QByteArray argument) {
|
void push(QByteArray argument) {
|
||||||
|
@ -45,7 +48,32 @@ private:
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Launcher::Launcher(int argc, char *argv[])
|
Launcher::Launcher(int argc, char *argv[])
|
||||||
: Core::Launcher(argc, argv) {
|
: Core::Launcher(argc, argv)
|
||||||
|
, _arguments(argv, argv + argc) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int Launcher::exec() {
|
||||||
|
for (auto i = begin(_arguments), e = end(_arguments); i != e; ++i) {
|
||||||
|
if (*i == "-basegtkintegration" && std::distance(i, e) > 2) {
|
||||||
|
return GtkIntegration::Exec(
|
||||||
|
GtkIntegration::Type::Base,
|
||||||
|
QString::fromStdString(*(i + 1)),
|
||||||
|
std::stoi(*(i + 2)));
|
||||||
|
} else if (*i == "-webviewhelper" && std::distance(i, e) > 3) {
|
||||||
|
return GtkIntegration::Exec(
|
||||||
|
GtkIntegration::Type::Webview,
|
||||||
|
QString::fromStdString(*(i + 1)),
|
||||||
|
std::stoi(*(i + 2)),
|
||||||
|
std::stoi(*(i + 3)));
|
||||||
|
} else if (*i == "-gtkintegration" && std::distance(i, e) > 2) {
|
||||||
|
return GtkIntegration::Exec(
|
||||||
|
GtkIntegration::Type::TDesktop,
|
||||||
|
QString::fromStdString(*(i + 1)),
|
||||||
|
std::stoi(*(i + 2)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Core::Launcher::exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::initHook() {
|
void Launcher::initHook() {
|
||||||
|
|
|
@ -15,10 +15,14 @@ class Launcher : public Core::Launcher {
|
||||||
public:
|
public:
|
||||||
Launcher(int argc, char *argv[]);
|
Launcher(int argc, char *argv[]);
|
||||||
|
|
||||||
|
int exec();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initHook() override;
|
void initHook() override;
|
||||||
bool launchUpdater(UpdaterLaunch action) override;
|
bool launchUpdater(UpdaterLaunch action) override;
|
||||||
|
|
||||||
|
std::vector<std::string> _arguments;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
|
|
|
@ -10,9 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/platform/linux/base_linux_gtk_integration.h"
|
#include "base/platform/linux/base_linux_gtk_integration.h"
|
||||||
#include "base/platform/linux/base_linux_gtk_integration_p.h"
|
#include "base/platform/linux/base_linux_gtk_integration_p.h"
|
||||||
#include "platform/linux/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
|
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -98,21 +95,17 @@ void GdkHelperLoad(QLibrary &lib) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdkSetTransientFor(GdkWindow *window, QWindow *parent) {
|
void GdkSetTransientFor(GdkWindow *window, const QString &parent) {
|
||||||
#ifndef DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION
|
#ifndef DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION
|
||||||
if (gdk_wayland_window_get_type != nullptr
|
if (gdk_wayland_window_get_type != nullptr
|
||||||
&& gdk_wayland_window_set_transient_for_exported != nullptr
|
&& gdk_wayland_window_set_transient_for_exported != nullptr
|
||||||
&& GDK_IS_WAYLAND_WINDOW(window)) {
|
&& GDK_IS_WAYLAND_WINDOW(window)
|
||||||
if (const auto integration = WaylandIntegration::Instance()) {
|
&& parent.startsWith("wayland:")) {
|
||||||
if (const auto handle = integration->nativeHandle(parent)
|
auto handle = parent.mid(8).toUtf8();
|
||||||
; !handle.isEmpty()) {
|
gdk_wayland_window_set_transient_for_exported(
|
||||||
auto handleUtf8 = handle.toUtf8();
|
window,
|
||||||
gdk_wayland_window_set_transient_for_exported(
|
handle.data());
|
||||||
window,
|
return;
|
||||||
handleUtf8.data());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif // !DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION
|
#endif // !DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION
|
||||||
|
|
||||||
|
@ -121,23 +114,33 @@ void GdkSetTransientFor(GdkWindow *window, QWindow *parent) {
|
||||||
&& gdk_x11_display_get_xdisplay != nullptr
|
&& gdk_x11_display_get_xdisplay != nullptr
|
||||||
&& gdk_x11_window_get_xid != nullptr
|
&& gdk_x11_window_get_xid != nullptr
|
||||||
&& gdk_window_get_display != nullptr
|
&& gdk_window_get_display != nullptr
|
||||||
&& GDK_IS_X11_WINDOW(window)) {
|
&& GDK_IS_X11_WINDOW(window)
|
||||||
XSetTransientForHint(
|
&& parent.startsWith("x11:")) {
|
||||||
gdk_x11_display_get_xdisplay(gdk_window_get_display(window)),
|
auto ok = false;
|
||||||
gdk_x11_window_get_xid(window),
|
const auto winId = parent.mid(4).toInt(&ok, 16);
|
||||||
parent->winId());
|
if (ok) {
|
||||||
return;
|
XSetTransientForHint(
|
||||||
|
gdk_x11_display_get_xdisplay(gdk_window_get_display(window)),
|
||||||
|
gdk_x11_window_get_xid(window),
|
||||||
|
winId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||||
|
|
||||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||||
if (gdk_x11_drawable_get_xdisplay != nullptr
|
if (gdk_x11_drawable_get_xdisplay != nullptr
|
||||||
&& gdk_x11_drawable_get_xid != nullptr) {
|
&& gdk_x11_drawable_get_xid != nullptr
|
||||||
XSetTransientForHint(
|
&& parent.startsWith("x11:")) {
|
||||||
gdk_x11_drawable_get_xdisplay(window),
|
auto ok = false;
|
||||||
gdk_x11_drawable_get_xid(window),
|
const auto winId = parent.mid(4).toInt(&ok, 16);
|
||||||
parent->winId());
|
if (ok) {
|
||||||
return;
|
XSetTransientForHint(
|
||||||
|
gdk_x11_drawable_get_xdisplay(window),
|
||||||
|
gdk_x11_drawable_get_xid(window),
|
||||||
|
winId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
class QLibrary;
|
class QLibrary;
|
||||||
class QWindow;
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
@ -19,7 +18,7 @@ namespace Platform {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
void GdkHelperLoad(QLibrary &lib);
|
void GdkHelperLoad(QLibrary &lib);
|
||||||
void GdkSetTransientFor(GdkWindow *window, QWindow *parent);
|
void GdkSetTransientFor(GdkWindow *window, const QString &parent);
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
|
|
|
@ -7,11 +7,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "platform/linux/linux_gtk_integration.h"
|
#include "platform/linux/linux_gtk_integration.h"
|
||||||
|
|
||||||
|
#ifdef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
|
#error "GTK integration depends on D-Bus integration."
|
||||||
|
#endif // DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||||
|
|
||||||
#include "base/platform/linux/base_linux_gtk_integration.h"
|
#include "base/platform/linux/base_linux_gtk_integration.h"
|
||||||
#include "base/platform/linux/base_linux_gtk_integration_p.h"
|
#include "base/platform/linux/base_linux_gtk_integration_p.h"
|
||||||
|
#include "base/platform/linux/base_linux_glibmm_helper.h"
|
||||||
|
#include "base/platform/linux/base_linux_dbus_utilities.h"
|
||||||
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "platform/linux/linux_gtk_integration_p.h"
|
#include "platform/linux/linux_gtk_integration_p.h"
|
||||||
#include "platform/linux/linux_gdk_helper.h"
|
#include "platform/linux/linux_gdk_helper.h"
|
||||||
#include "platform/linux/linux_gtk_open_with_dialog.h"
|
#include "platform/linux/linux_gtk_open_with_dialog.h"
|
||||||
|
#include "platform/linux/linux_wayland_integration.h"
|
||||||
|
#include "webview/webview_interface.h"
|
||||||
|
#include "window/window_controller.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
|
||||||
|
#include <QtCore/QProcess>
|
||||||
|
|
||||||
|
#include <private/qguiapplication_p.h>
|
||||||
|
#include <giomm.h>
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
@ -21,6 +41,31 @@ using BaseGtkIntegration = base::Platform::GtkIntegration;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kService = "org.telegram.desktop.GtkIntegration-%1"_cs;
|
||||||
|
constexpr auto kObjectPath = "/org/telegram/desktop/GtkIntegration"_cs;
|
||||||
|
constexpr auto kInterface = "org.telegram.desktop.GtkIntegration"_cs;
|
||||||
|
constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs;
|
||||||
|
constexpr auto kGifcShmId = "tdesktop-gtk-gifc"_cs;
|
||||||
|
|
||||||
|
constexpr auto kIntrospectionXML = R"INTROSPECTION(<node>
|
||||||
|
<interface name='org.telegram.desktop.GtkIntegration'>
|
||||||
|
<method name='Load'>
|
||||||
|
<arg type='s' name='allowed-backends' direction='in'/>
|
||||||
|
</method>
|
||||||
|
<method name='ShowOpenWithDialog'>
|
||||||
|
<arg type='s' name='parent' direction='in'/>
|
||||||
|
<arg type='s' name='filepath' direction='in'/>
|
||||||
|
</method>
|
||||||
|
<method name='GetImageFromClipboard'>
|
||||||
|
<arg type='h' name='shm-descriptor' direction='out'/>
|
||||||
|
<arg type='i' name='shm-size' direction='out'/>
|
||||||
|
</method>
|
||||||
|
<signal name='OpenWithDialogResponse'>
|
||||||
|
<arg type='b' name='result' direction='out'/>
|
||||||
|
</signal>
|
||||||
|
</interface>
|
||||||
|
</node>)INTROSPECTION"_cs;
|
||||||
|
|
||||||
bool GetImageFromClipboardSupported() {
|
bool GetImageFromClipboardSupported() {
|
||||||
return (gtk_clipboard_get != nullptr)
|
return (gtk_clipboard_get != nullptr)
|
||||||
&& (gtk_clipboard_wait_for_contents != nullptr)
|
&& (gtk_clipboard_wait_for_contents != nullptr)
|
||||||
|
@ -37,7 +82,182 @@ bool GetImageFromClipboardSupported() {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
GtkIntegration::GtkIntegration() {
|
class GtkIntegration::Private {
|
||||||
|
public:
|
||||||
|
Private()
|
||||||
|
: dbusConnection([] {
|
||||||
|
try {
|
||||||
|
return Gio::DBus::Connection::get_sync(
|
||||||
|
Gio::DBus::BusType::BUS_TYPE_SESSION);
|
||||||
|
} catch (...) {
|
||||||
|
return Glib::RefPtr<Gio::DBus::Connection>();
|
||||||
|
}
|
||||||
|
}())
|
||||||
|
, interfaceVTable(sigc::mem_fun(this, &Private::handleMethodCall))
|
||||||
|
, serviceName(kService.utf16().arg(getpid()).toStdString()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleMethodCall(
|
||||||
|
const Glib::RefPtr<Gio::DBus::Connection> &connection,
|
||||||
|
const Glib::ustring &sender,
|
||||||
|
const Glib::ustring &object_path,
|
||||||
|
const Glib::ustring &interface_name,
|
||||||
|
const Glib::ustring &method_name,
|
||||||
|
const Glib::VariantContainerBase ¶meters,
|
||||||
|
const Glib::RefPtr<Gio::DBus::MethodInvocation> &invocation);
|
||||||
|
|
||||||
|
const Glib::RefPtr<Gio::DBus::Connection> dbusConnection;
|
||||||
|
const Gio::DBus::InterfaceVTable interfaceVTable;
|
||||||
|
Glib::RefPtr<Gio::DBus::NodeInfo> introspectionData;
|
||||||
|
Glib::ustring serviceName;
|
||||||
|
Glib::ustring parentDBusName;
|
||||||
|
bool remoting = true;
|
||||||
|
uint registerId = 0;
|
||||||
|
uint parentServiceWatcherId = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void GtkIntegration::Private::handleMethodCall(
|
||||||
|
const Glib::RefPtr<Gio::DBus::Connection> &connection,
|
||||||
|
const Glib::ustring &sender,
|
||||||
|
const Glib::ustring &object_path,
|
||||||
|
const Glib::ustring &interface_name,
|
||||||
|
const Glib::ustring &method_name,
|
||||||
|
const Glib::VariantContainerBase ¶meters,
|
||||||
|
const Glib::RefPtr<Gio::DBus::MethodInvocation> &invocation) {
|
||||||
|
if (sender != parentDBusName) {
|
||||||
|
Gio::DBus::Error error(
|
||||||
|
Gio::DBus::Error::ACCESS_DENIED,
|
||||||
|
"Access denied.");
|
||||||
|
|
||||||
|
invocation->return_error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const auto integration = Instance();
|
||||||
|
if (!integration) {
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto parametersCopy = parameters;
|
||||||
|
|
||||||
|
if (method_name == "Load") {
|
||||||
|
const auto allowedBackends = base::Platform::GlibVariantCast<
|
||||||
|
Glib::ustring>(parametersCopy.get_child(0));
|
||||||
|
|
||||||
|
integration->load(QString::fromStdString(allowedBackends));
|
||||||
|
invocation->return_value({});
|
||||||
|
return;
|
||||||
|
} else if (method_name == "ShowOpenWithDialog") {
|
||||||
|
const auto parent = base::Platform::GlibVariantCast<
|
||||||
|
Glib::ustring>(parametersCopy.get_child(0));
|
||||||
|
|
||||||
|
const auto filepath = base::Platform::GlibVariantCast<
|
||||||
|
Glib::ustring>(parametersCopy.get_child(1));
|
||||||
|
|
||||||
|
const auto result = File::internal::ShowGtkOpenWithDialog(
|
||||||
|
QString::fromStdString(parent),
|
||||||
|
QString::fromStdString(filepath));
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
invocation->return_value({});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (method_name == "GetImageFromClipboard") {
|
||||||
|
const auto image = integration->getImageFromClipboard();
|
||||||
|
if (!image.isNull()) {
|
||||||
|
const auto bitsPerSample = 8;
|
||||||
|
const auto channels = image.hasAlphaChannel() ? 4 : 3;
|
||||||
|
|
||||||
|
QVector<uchar> dataVector(
|
||||||
|
image.constBits(),
|
||||||
|
image.constBits() + image.sizeInBytes());
|
||||||
|
|
||||||
|
QByteArray streamData;
|
||||||
|
QDataStream stream(&streamData, QIODevice::WriteOnly);
|
||||||
|
stream
|
||||||
|
<< image.width()
|
||||||
|
<< image.height()
|
||||||
|
<< image.bytesPerLine()
|
||||||
|
<< image.hasAlphaChannel()
|
||||||
|
<< bitsPerSample
|
||||||
|
<< channels
|
||||||
|
<< dataVector;
|
||||||
|
|
||||||
|
const auto fd = shm_open(
|
||||||
|
kGifcShmId.utf8().constData(),
|
||||||
|
O_RDWR | O_CREAT,
|
||||||
|
S_IRUSR | S_IWUSR);
|
||||||
|
|
||||||
|
if (fd == -1) {
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto fdGuard = gsl::finally([&] {
|
||||||
|
close(fd);
|
||||||
|
shm_unlink(kGifcShmId.utf8().constData());
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ftruncate(fd, streamData.size())) {
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto mapped = mmap(
|
||||||
|
nullptr,
|
||||||
|
streamData.size(),
|
||||||
|
PROT_WRITE,
|
||||||
|
MAP_SHARED,
|
||||||
|
fd,
|
||||||
|
0);
|
||||||
|
|
||||||
|
if (mapped == MAP_FAILED) {
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto mappedGuard = gsl::finally([&] {
|
||||||
|
munmap(mapped, streamData.size());
|
||||||
|
});
|
||||||
|
|
||||||
|
memcpy(mapped, streamData.constData(), streamData.size());
|
||||||
|
|
||||||
|
const auto fdList = Gio::UnixFDList::create();
|
||||||
|
fdList->append(fd);
|
||||||
|
|
||||||
|
invocation->return_value(
|
||||||
|
Glib::VariantContainerBase::create_tuple({
|
||||||
|
Glib::wrap(g_variant_new_handle(0)),
|
||||||
|
Glib::Variant<int>::create(streamData.size()),
|
||||||
|
}),
|
||||||
|
fdList);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Gio::DBus::Error error(
|
||||||
|
Gio::DBus::Error::UNKNOWN_METHOD,
|
||||||
|
"Method does not exist.");
|
||||||
|
|
||||||
|
invocation->return_error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkIntegration::GtkIntegration()
|
||||||
|
: _private(std::make_unique<Private>()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkIntegration::~GtkIntegration() {
|
||||||
|
if (_private->dbusConnection) {
|
||||||
|
if (_private->parentServiceWatcherId != 0) {
|
||||||
|
_private->dbusConnection->signal_unsubscribe(
|
||||||
|
_private->parentServiceWatcherId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_private->registerId != 0) {
|
||||||
|
_private->dbusConnection->unregister_object(
|
||||||
|
_private->registerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GtkIntegration *GtkIntegration::Instance() {
|
GtkIntegration *GtkIntegration::Instance() {
|
||||||
|
@ -49,10 +269,31 @@ GtkIntegration *GtkIntegration::Instance() {
|
||||||
return &instance;
|
return &instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GtkIntegration::load() {
|
void GtkIntegration::load(const QString &allowedBackends) {
|
||||||
static bool Loaded = false;
|
static bool Loaded = false;
|
||||||
Expects(!Loaded);
|
Expects(!Loaded);
|
||||||
|
|
||||||
|
if (_private->remoting) {
|
||||||
|
if (!_private->dbusConnection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto reply = _private->dbusConnection->call_sync(
|
||||||
|
std::string(kObjectPath),
|
||||||
|
std::string(kInterface),
|
||||||
|
"Load",
|
||||||
|
base::Platform::MakeGlibVariant(std::tuple{
|
||||||
|
Glib::ustring(allowedBackends.toStdString()),
|
||||||
|
}),
|
||||||
|
_private->serviceName);
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseGtkIntegration::Instance()->load(allowedBackends, true);
|
||||||
if (!BaseGtkIntegration::Instance()->loaded()) {
|
if (!BaseGtkIntegration::Instance()->loaded()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -86,13 +327,233 @@ void GtkIntegration::load() {
|
||||||
Loaded = true;
|
Loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GtkIntegration::exec(const QString &parentDBusName, int ppid) {
|
||||||
|
_private->remoting = false;
|
||||||
|
_private->serviceName = kService.utf16().arg(ppid).toStdString();
|
||||||
|
_private->parentDBusName = parentDBusName.toStdString();
|
||||||
|
|
||||||
|
_private->introspectionData = Gio::DBus::NodeInfo::create_for_xml(
|
||||||
|
std::string(kIntrospectionXML));
|
||||||
|
|
||||||
|
_private->registerId = _private->dbusConnection->register_object(
|
||||||
|
std::string(kObjectPath),
|
||||||
|
_private->introspectionData->lookup_interface(),
|
||||||
|
_private->interfaceVTable);
|
||||||
|
|
||||||
|
rpl::lifetime lifetime;
|
||||||
|
|
||||||
|
File::internal::GtkOpenWithDialogResponse(
|
||||||
|
) | rpl::start_with_next([=](bool response) {
|
||||||
|
try {
|
||||||
|
_private->dbusConnection->emit_signal(
|
||||||
|
std::string(kObjectPath),
|
||||||
|
std::string(kInterface),
|
||||||
|
"OpenWithDialogResponse",
|
||||||
|
_private->parentDBusName,
|
||||||
|
base::Platform::MakeGlibVariant(std::tuple{
|
||||||
|
response,
|
||||||
|
}));
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
}, lifetime);
|
||||||
|
|
||||||
|
const auto app = Gio::Application::create(_private->serviceName);
|
||||||
|
app->hold();
|
||||||
|
_private->parentServiceWatcherId = base::Platform::DBus::RegisterServiceWatcher(
|
||||||
|
_private->dbusConnection,
|
||||||
|
parentDBusName.toStdString(),
|
||||||
|
[=](
|
||||||
|
const Glib::ustring &service,
|
||||||
|
const Glib::ustring &oldOwner,
|
||||||
|
const Glib::ustring &newOwner) {
|
||||||
|
if (!newOwner.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
app->quit();
|
||||||
|
});
|
||||||
|
return app->run(0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
bool GtkIntegration::showOpenWithDialog(const QString &filepath) const {
|
bool GtkIntegration::showOpenWithDialog(const QString &filepath) const {
|
||||||
return File::internal::ShowGtkOpenWithDialog(filepath);
|
const auto parent = [&] {
|
||||||
|
if (const auto activeWindow = Core::App().activeWindow()) {
|
||||||
|
if (const auto integration = WaylandIntegration::Instance()) {
|
||||||
|
if (const auto handle = integration->nativeHandle(
|
||||||
|
activeWindow->widget()->windowHandle())
|
||||||
|
; !handle.isEmpty()) {
|
||||||
|
return qsl("wayland:") + handle;
|
||||||
|
}
|
||||||
|
} else if (Platform::IsX11()) {
|
||||||
|
return qsl("x11:") + QString::number(
|
||||||
|
activeWindow->widget()->winId(),
|
||||||
|
16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (_private->remoting) {
|
||||||
|
if (!_private->dbusConnection) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_private->dbusConnection->call_sync(
|
||||||
|
std::string(kObjectPath),
|
||||||
|
std::string(kInterface),
|
||||||
|
"ShowOpenWithDialog",
|
||||||
|
base::Platform::MakeGlibVariant(std::tuple{
|
||||||
|
Glib::ustring(parent.toStdString()),
|
||||||
|
Glib::ustring(filepath.toStdString()),
|
||||||
|
}),
|
||||||
|
_private->serviceName);
|
||||||
|
|
||||||
|
const auto context = Glib::MainContext::create();
|
||||||
|
const auto loop = Glib::MainLoop::create(context);
|
||||||
|
g_main_context_push_thread_default(context->gobj());
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
const auto signalId = _private->dbusConnection->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,
|
||||||
|
Glib::VariantContainerBase parameters) {
|
||||||
|
try {
|
||||||
|
auto parametersCopy = parameters;
|
||||||
|
|
||||||
|
result = base::Platform::GlibVariantCast<bool>(
|
||||||
|
parametersCopy.get_child(0));
|
||||||
|
|
||||||
|
loop->quit();
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_private->serviceName,
|
||||||
|
std::string(kInterface),
|
||||||
|
"OpenWithDialogResponse",
|
||||||
|
std::string(kObjectPath));
|
||||||
|
|
||||||
|
const auto signalGuard = gsl::finally([&] {
|
||||||
|
if (signalId != 0) {
|
||||||
|
_private->dbusConnection->signal_unsubscribe(signalId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QWindow window;
|
||||||
|
QGuiApplicationPrivate::showModalWindow(&window);
|
||||||
|
loop->run();
|
||||||
|
g_main_context_pop_thread_default(context->gobj());
|
||||||
|
QGuiApplicationPrivate::hideModalWindow(&window);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File::internal::ShowGtkOpenWithDialog(parent, filepath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto context = Glib::MainContext::create();
|
||||||
|
const auto loop = Glib::MainLoop::create(context);
|
||||||
|
g_main_context_push_thread_default(context->gobj());
|
||||||
|
rpl::lifetime lifetime;
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
File::internal::GtkOpenWithDialogResponse(
|
||||||
|
) | rpl::start_with_next([&](bool response) {
|
||||||
|
result = response;
|
||||||
|
loop->quit();
|
||||||
|
}, lifetime);
|
||||||
|
|
||||||
|
QWindow window;
|
||||||
|
QGuiApplicationPrivate::showModalWindow(&window);
|
||||||
|
loop->run();
|
||||||
|
g_main_context_pop_thread_default(context->gobj());
|
||||||
|
QGuiApplicationPrivate::hideModalWindow(&window);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage GtkIntegration::getImageFromClipboard() const {
|
QImage GtkIntegration::getImageFromClipboard() const {
|
||||||
QImage data;
|
QImage data;
|
||||||
|
|
||||||
|
if (_private->remoting) {
|
||||||
|
if (!_private->dbusConnection) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Glib::RefPtr<Gio::UnixFDList> outFdList;
|
||||||
|
|
||||||
|
auto reply = _private->dbusConnection->call_sync(
|
||||||
|
std::string(kObjectPath),
|
||||||
|
std::string(kInterface),
|
||||||
|
"GetImageFromClipboard",
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
outFdList,
|
||||||
|
_private->serviceName);
|
||||||
|
|
||||||
|
const auto streamSize = base::Platform::GlibVariantCast<int>(
|
||||||
|
reply.get_child(1));
|
||||||
|
|
||||||
|
const auto mapped = mmap(
|
||||||
|
nullptr,
|
||||||
|
streamSize,
|
||||||
|
PROT_READ,
|
||||||
|
MAP_SHARED,
|
||||||
|
outFdList->get(0),
|
||||||
|
0);
|
||||||
|
|
||||||
|
if (mapped == MAP_FAILED) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray streamData;
|
||||||
|
streamData.resize(streamSize);
|
||||||
|
memcpy(streamData.data(), mapped, streamData.size());
|
||||||
|
munmap(mapped, streamData.size());
|
||||||
|
|
||||||
|
int imageWidth = 0;
|
||||||
|
int imageHeight = 0;
|
||||||
|
int imageBytesPerLine = 0;
|
||||||
|
bool imageHasAlphaChannel = false;
|
||||||
|
int imageBitsPerSample = 0;
|
||||||
|
int imageChannels = 0;
|
||||||
|
QVector<uchar> imageData;
|
||||||
|
|
||||||
|
QDataStream stream(streamData);
|
||||||
|
stream
|
||||||
|
>> imageWidth
|
||||||
|
>> imageHeight
|
||||||
|
>> imageBytesPerLine
|
||||||
|
>> imageHasAlphaChannel
|
||||||
|
>> imageBitsPerSample
|
||||||
|
>> imageChannels
|
||||||
|
>> imageData;
|
||||||
|
|
||||||
|
data = QImage(
|
||||||
|
imageData.data(),
|
||||||
|
imageWidth,
|
||||||
|
imageHeight,
|
||||||
|
imageBytesPerLine,
|
||||||
|
imageHasAlphaChannel
|
||||||
|
? QImage::Format_RGBA8888
|
||||||
|
: QImage::Format_RGB888).copy();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
if (!GetImageFromClipboardSupported()) {
|
if (!GetImageFromClipboardSupported()) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -130,5 +591,113 @@ QImage GtkIntegration::getImageFromClipboard() const {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString GtkIntegration::AllowedBackends() {
|
||||||
|
return Platform::IsWayland()
|
||||||
|
? qsl("wayland,x11")
|
||||||
|
: Platform::IsX11()
|
||||||
|
? qsl("x11,wayland")
|
||||||
|
: QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int GtkIntegration::Exec(
|
||||||
|
Type type,
|
||||||
|
const QString &parentDBusName,
|
||||||
|
int ppid,
|
||||||
|
uint instanceNumber) {
|
||||||
|
Glib::init();
|
||||||
|
Gio::init();
|
||||||
|
|
||||||
|
if (type == Type::Base) {
|
||||||
|
if (const auto integration = BaseGtkIntegration::Instance()) {
|
||||||
|
return integration->exec(parentDBusName, ppid);
|
||||||
|
}
|
||||||
|
} else if (type == Type::Webview) {
|
||||||
|
if (const auto instance = Webview::CreateInstance({})) {
|
||||||
|
return instance->exec(
|
||||||
|
parentDBusName.toStdString(),
|
||||||
|
ppid,
|
||||||
|
instanceNumber);
|
||||||
|
}
|
||||||
|
} else if (type == Type::TDesktop) {
|
||||||
|
if (const auto integration = Instance()) {
|
||||||
|
return integration->exec(parentDBusName, ppid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkIntegration::Start(Type type) {
|
||||||
|
if (type != Type::Base && type != Type::TDesktop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto dbusName = [] {
|
||||||
|
try {
|
||||||
|
static const auto connection = Gio::DBus::Connection::get_sync(
|
||||||
|
Gio::DBus::BusType::BUS_TYPE_SESSION);
|
||||||
|
|
||||||
|
return QString::fromStdString(connection->get_unique_name());
|
||||||
|
} catch (...) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (dbusName.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QProcess::startDetached(cExeDir() + cExeName(), {
|
||||||
|
(type == Type::Base)
|
||||||
|
? qsl("-basegtkintegration")
|
||||||
|
: qsl("-gtkintegration"),
|
||||||
|
dbusName,
|
||||||
|
QString::number(getpid()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkIntegration::Autorestart(Type type) {
|
||||||
|
if (type != Type::Base && type != Type::TDesktop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
static const auto connection = Gio::DBus::Connection::get_sync(
|
||||||
|
Gio::DBus::BusType::BUS_TYPE_SESSION);
|
||||||
|
|
||||||
|
const auto baseServiceName = [] {
|
||||||
|
if (const auto integration = BaseGtkIntegration::Instance()) {
|
||||||
|
return integration->serviceName();
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}();
|
||||||
|
|
||||||
|
base::Platform::DBus::RegisterServiceWatcher(
|
||||||
|
connection,
|
||||||
|
(type == Type::Base)
|
||||||
|
? baseServiceName.toStdString()
|
||||||
|
: kService.utf16().arg(getpid()).toStdString(),
|
||||||
|
[=](
|
||||||
|
const Glib::ustring &service,
|
||||||
|
const Glib::ustring &oldOwner,
|
||||||
|
const Glib::ustring &newOwner) {
|
||||||
|
if (newOwner.empty()) {
|
||||||
|
Start(type);
|
||||||
|
} else {
|
||||||
|
if (type == Type::Base) {
|
||||||
|
if (const auto integration = BaseGtkIntegration::Instance()) {
|
||||||
|
integration->load(AllowedBackends());
|
||||||
|
}
|
||||||
|
} else if (type == Type::TDesktop) {
|
||||||
|
if (const auto integration = Instance()) {
|
||||||
|
integration->load(AllowedBackends());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
|
|
|
@ -12,16 +12,39 @@ namespace internal {
|
||||||
|
|
||||||
class GtkIntegration {
|
class GtkIntegration {
|
||||||
public:
|
public:
|
||||||
|
enum class Type {
|
||||||
|
Base,
|
||||||
|
Webview,
|
||||||
|
TDesktop,
|
||||||
|
};
|
||||||
|
|
||||||
static GtkIntegration *Instance();
|
static GtkIntegration *Instance();
|
||||||
|
|
||||||
void load();
|
void load(const QString &allowedBackends);
|
||||||
|
int exec(const QString &parentDBusName, int ppid);
|
||||||
|
|
||||||
[[nodiscard]] bool showOpenWithDialog(const QString &filepath) const;
|
[[nodiscard]] bool showOpenWithDialog(const QString &filepath) const;
|
||||||
|
|
||||||
[[nodiscard]] QImage getImageFromClipboard() const;
|
[[nodiscard]] QImage getImageFromClipboard() const;
|
||||||
|
|
||||||
|
static QString AllowedBackends();
|
||||||
|
|
||||||
|
static int Exec(
|
||||||
|
Type type,
|
||||||
|
const QString &parentDBusName,
|
||||||
|
int ppid,
|
||||||
|
uint instanceNumber = 0);
|
||||||
|
|
||||||
|
static void Start(Type type);
|
||||||
|
|
||||||
|
static void Autorestart(Type type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GtkIntegration();
|
GtkIntegration();
|
||||||
|
~GtkIntegration();
|
||||||
|
|
||||||
|
class Private;
|
||||||
|
const std::unique_ptr<Private> _private;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
|
@ -10,14 +10,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
class GtkIntegration::Private {
|
||||||
|
};
|
||||||
|
|
||||||
GtkIntegration::GtkIntegration() {
|
GtkIntegration::GtkIntegration() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GtkIntegration::~GtkIntegration() = default;
|
||||||
|
|
||||||
GtkIntegration *GtkIntegration::Instance() {
|
GtkIntegration *GtkIntegration::Instance() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GtkIntegration::load() {
|
void GtkIntegration::load(const QString &allowedBackends) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int GtkIntegration::exec(const QString &parentDBusName, int ppid) {
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GtkIntegration::showOpenWithDialog(const QString &filepath) const {
|
bool GtkIntegration::showOpenWithDialog(const QString &filepath) const {
|
||||||
|
@ -28,5 +37,23 @@ QImage GtkIntegration::getImageFromClipboard() const {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString GtkIntegration::AllowedBackends() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int GtkIntegration:Exec(
|
||||||
|
Type type,
|
||||||
|
const QString &parentDBusName,
|
||||||
|
int ppid,
|
||||||
|
uint instanceNumber) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkIntegration::Start(Type type) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void GtkIntegration::Autorestart(Type type) {
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace Platform
|
} // namespace Platform
|
||||||
|
|
|
@ -9,10 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "platform/linux/linux_gtk_integration_p.h"
|
#include "platform/linux/linux_gtk_integration_p.h"
|
||||||
#include "platform/linux/linux_gdk_helper.h"
|
#include "platform/linux/linux_gdk_helper.h"
|
||||||
#include "window/window_controller.h"
|
|
||||||
#include "core/application.h"
|
|
||||||
|
|
||||||
#include <private/qguiapplication_p.h>
|
|
||||||
#include <giomm.h>
|
#include <giomm.h>
|
||||||
|
|
||||||
namespace Platform {
|
namespace Platform {
|
||||||
|
@ -22,6 +19,8 @@ namespace {
|
||||||
|
|
||||||
using namespace Platform::Gtk;
|
using namespace Platform::Gtk;
|
||||||
|
|
||||||
|
rpl::event_stream<bool> GtkOpenWithDialogResponseStream;
|
||||||
|
|
||||||
struct GtkWidgetDeleter {
|
struct GtkWidgetDeleter {
|
||||||
void operator()(GtkWidget *widget) {
|
void operator()(GtkWidget *widget) {
|
||||||
gtk_widget_destroy(widget);
|
gtk_widget_destroy(widget);
|
||||||
|
@ -38,22 +37,22 @@ bool Supported() {
|
||||||
&& (gtk_widget_destroy != nullptr);
|
&& (gtk_widget_destroy != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
class GtkOpenWithDialog : public QWindow {
|
class GtkOpenWithDialog {
|
||||||
public:
|
public:
|
||||||
GtkOpenWithDialog(const QString &filepath);
|
GtkOpenWithDialog(
|
||||||
|
const QString &parent,
|
||||||
bool exec();
|
const QString &filepath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void handleResponse(GtkOpenWithDialog *dialog, int responseId);
|
static void handleResponse(GtkOpenWithDialog *dialog, int responseId);
|
||||||
|
|
||||||
const Glib::RefPtr<Gio::File> _file;
|
const Glib::RefPtr<Gio::File> _file;
|
||||||
const std::unique_ptr<GtkWidget, GtkWidgetDeleter> _gtkWidget;
|
const std::unique_ptr<GtkWidget, GtkWidgetDeleter> _gtkWidget;
|
||||||
QEventLoop _loop;
|
|
||||||
std::optional<bool> _result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
GtkOpenWithDialog::GtkOpenWithDialog(const QString &filepath)
|
GtkOpenWithDialog::GtkOpenWithDialog(
|
||||||
|
const QString &parent,
|
||||||
|
const QString &filepath)
|
||||||
: _file(Gio::File::create_for_path(filepath.toStdString()))
|
: _file(Gio::File::create_for_path(filepath.toStdString()))
|
||||||
, _gtkWidget(gtk_app_chooser_dialog_new(
|
, _gtkWidget(gtk_app_chooser_dialog_new(
|
||||||
nullptr,
|
nullptr,
|
||||||
|
@ -64,31 +63,19 @@ GtkOpenWithDialog::GtkOpenWithDialog(const QString &filepath)
|
||||||
"response",
|
"response",
|
||||||
G_CALLBACK(handleResponse),
|
G_CALLBACK(handleResponse),
|
||||||
this);
|
this);
|
||||||
}
|
|
||||||
|
|
||||||
bool GtkOpenWithDialog::exec() {
|
|
||||||
gtk_widget_realize(_gtkWidget.get());
|
gtk_widget_realize(_gtkWidget.get());
|
||||||
|
|
||||||
if (const auto activeWindow = Core::App().activeWindow()) {
|
Platform::internal::GdkSetTransientFor(
|
||||||
Platform::internal::GdkSetTransientFor(
|
gtk_widget_get_window(_gtkWidget.get()),
|
||||||
gtk_widget_get_window(_gtkWidget.get()),
|
parent);
|
||||||
activeWindow->widget()->windowHandle());
|
|
||||||
}
|
|
||||||
|
|
||||||
QGuiApplicationPrivate::showModalWindow(this);
|
|
||||||
gtk_widget_show(_gtkWidget.get());
|
gtk_widget_show(_gtkWidget.get());
|
||||||
|
|
||||||
if (!_result.has_value()) {
|
|
||||||
_loop.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
QGuiApplicationPrivate::hideModalWindow(this);
|
|
||||||
return *_result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GtkOpenWithDialog::handleResponse(GtkOpenWithDialog *dialog, int responseId) {
|
void GtkOpenWithDialog::handleResponse(GtkOpenWithDialog *dialog, int responseId) {
|
||||||
Glib::RefPtr<Gio::AppInfo> chosenAppInfo;
|
Glib::RefPtr<Gio::AppInfo> chosenAppInfo;
|
||||||
dialog->_result = true;
|
bool result = true;
|
||||||
|
|
||||||
switch (responseId) {
|
switch (responseId) {
|
||||||
case GTK_RESPONSE_OK:
|
case GTK_RESPONSE_OK:
|
||||||
|
@ -110,21 +97,28 @@ void GtkOpenWithDialog::handleResponse(GtkOpenWithDialog *dialog, int responseId
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
dialog->_result = false;
|
result = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog->_loop.quit();
|
GtkOpenWithDialogResponseStream.fire_copy(result);
|
||||||
|
delete dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool ShowGtkOpenWithDialog(const QString &filepath) {
|
bool ShowGtkOpenWithDialog(
|
||||||
|
const QString &parent,
|
||||||
|
const QString &filepath) {
|
||||||
if (!Supported()) {
|
if (!Supported()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GtkOpenWithDialog(filepath).exec();
|
return new GtkOpenWithDialog(parent, filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<bool> GtkOpenWithDialogResponse() {
|
||||||
|
return GtkOpenWithDialogResponseStream.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
|
@ -11,7 +11,11 @@ namespace Platform {
|
||||||
namespace File {
|
namespace File {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
bool ShowGtkOpenWithDialog(const QString &filepath);
|
bool ShowGtkOpenWithDialog(
|
||||||
|
const QString &parent,
|
||||||
|
const QString &filepath);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<bool> GtkOpenWithDialogResponse();
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace File
|
} // namespace File
|
||||||
|
|
|
@ -743,11 +743,8 @@ void start() {
|
||||||
Glib::set_prgname(cExeName().toStdString());
|
Glib::set_prgname(cExeName().toStdString());
|
||||||
Glib::set_application_name(std::string(AppName));
|
Glib::set_application_name(std::string(AppName));
|
||||||
|
|
||||||
if (const auto integration = BaseGtkIntegration::Instance()) {
|
GtkIntegration::Start(GtkIntegration::Type::Base);
|
||||||
integration->prepareEnvironment();
|
GtkIntegration::Start(GtkIntegration::Type::TDesktop);
|
||||||
} else {
|
|
||||||
g_warning("GTK integration is disabled, some features unavailable.");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DESKTOP_APP_USE_PACKAGED_RLOTTIE
|
#ifdef DESKTOP_APP_USE_PACKAGED_RLOTTIE
|
||||||
g_warning(
|
g_warning(
|
||||||
|
@ -944,13 +941,16 @@ bool OpenSystemSettings(SystemSettingsType type) {
|
||||||
namespace ThirdParty {
|
namespace ThirdParty {
|
||||||
|
|
||||||
void start() {
|
void start() {
|
||||||
|
GtkIntegration::Autorestart(GtkIntegration::Type::Base);
|
||||||
|
GtkIntegration::Autorestart(GtkIntegration::Type::TDesktop);
|
||||||
|
|
||||||
if (const auto integration = BaseGtkIntegration::Instance()) {
|
if (const auto integration = BaseGtkIntegration::Instance()) {
|
||||||
integration->load();
|
integration->load(GtkIntegration::AllowedBackends());
|
||||||
integration->initializeSettings();
|
integration->initializeSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto integration = GtkIntegration::Instance()) {
|
if (const auto integration = GtkIntegration::Instance()) {
|
||||||
integration->load();
|
integration->load(GtkIntegration::AllowedBackends());
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for interface announce to know if native window frame is supported
|
// wait for interface announce to know if native window frame is supported
|
||||||
|
|
Loading…
Reference in New Issue