Use gtk clipboard when available to avoid https://bugreports.qt.io/browse/QTBUG-56595

This commit is contained in:
Ilya Fedin 2020-06-10 00:36:51 +04:00 committed by John Preston
parent a70cc9b956
commit 3a91003eea
15 changed files with 177 additions and 23 deletions

View File

@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document_media.h"
#include "history/history.h"
#include "history/history_item.h"
#include "platform/platform_specific.h"
#include "lang/lang_keys.h"
#include "layout.h"
#include "media/streaming/media_streaming_instance.h"
@ -656,7 +657,10 @@ bool EditCaptionBox::fileFromClipboard(not_null<const QMimeData*> data) {
if (result.error == Error::None) {
return result;
} else if (data->hasImage()) {
auto image = qvariant_cast<QImage>(data->imageData());
auto image = Platform::GetImageFromClipboard();
if (image.isNull()) {
image = qvariant_cast<QImage>(data->imageData());
}
if (!image.isNull()) {
_isImage = true;
_photo = true;

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "boxes/send_files_box.h"
#include "platform/platform_specific.h"
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "storage/storage_media_prepare.h"
@ -2168,7 +2169,10 @@ bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
if (result.error == Storage::PreparedList::Error::None) {
return result;
} else if (data->hasImage()) {
auto image = qvariant_cast<QImage>(data->imageData());
auto image = Platform::GetImageFromClipboard();
if (image.isNull()) {
image = qvariant_cast<QImage>(data->imageData());
}
if (!image.isNull()) {
return Storage::PrepareMediaFromImage(
std::move(image),

View File

@ -63,6 +63,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/tabbed_section.h"
#include "chat_helpers/bot_keyboard.h"
#include "chat_helpers/message_field.h"
#include "platform/platform_specific.h"
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "mainwindow.h"
@ -4512,7 +4513,10 @@ bool HistoryWidget::confirmSendingFiles(
}
if (hasImage) {
auto image = qvariant_cast<QImage>(data->imageData());
auto image = Platform::GetImageFromClipboard();
if (image.isNull()) {
image = qvariant_cast<QImage>(data->imageData());
}
if (!image.isNull()) {
confirmSendingFiles(
std::move(image),

View File

@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/storage_media_prepare.h"
#include "storage/localstorage.h"
#include "inline_bots/inline_bot_result.h"
#include "platform/platform_specific.h"
#include "lang/lang_keys.h"
#include "facades.h"
#include "app.h"
@ -265,7 +266,10 @@ bool ScheduledWidget::confirmSendingFiles(
}
if (hasImage) {
auto image = qvariant_cast<QImage>(data->imageData());
auto image = Platform::GetImageFromClipboard();
if (image.isNull()) {
image = qvariant_cast<QImage>(data->imageData());
}
if (!image.isNull()) {
confirmSendingFiles(
std::move(image),

View File

@ -72,10 +72,10 @@ bool GdkHelperLoadGtk3(QLibrary &lib) {
void GdkHelperLoad(QLibrary &lib) {
gdk_helper_loaded = GtkLoaded::GtkNone;
if (GdkHelperLoadGtk2(lib)) {
gdk_helper_loaded = GtkLoaded::Gtk2;
} else if (GdkHelperLoadGtk3(lib)) {
if (GdkHelperLoadGtk3(lib)) {
gdk_helper_loaded = GtkLoaded::Gtk3;
} else if (GdkHelperLoadGtk2(lib)) {
gdk_helper_loaded = GtkLoaded::Gtk2;
}
}

View File

@ -87,6 +87,10 @@ bool setupGtkBase(QLibrary &lib_gtk) {
if (!load(lib_gtk, "gtk_widget_destroy", gtk_widget_destroy)) return false;
if (!load(lib_gtk, "gtk_clipboard_get", gtk_clipboard_get)) return false;
if (!load(lib_gtk, "gtk_clipboard_store", gtk_clipboard_store)) return false;
if (!load(lib_gtk, "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents)) return false;
if (!load(lib_gtk, "gtk_clipboard_wait_for_image", gtk_clipboard_wait_for_image)) return false;
if (!load(lib_gtk, "gtk_selection_data_targets_include_image", gtk_selection_data_targets_include_image)) return false;
if (!load(lib_gtk, "gtk_selection_data_free", gtk_selection_data_free)) return false;
if (!load(lib_gtk, "gtk_file_chooser_dialog_new", gtk_file_chooser_dialog_new)) return false;
if (!load(lib_gtk, "gtk_file_chooser_get_type", gtk_file_chooser_get_type)) return false;
if (!load(lib_gtk, "gtk_image_get_type", gtk_image_get_type)) return false;
@ -138,6 +142,13 @@ bool setupGtkBase(QLibrary &lib_gtk) {
if (!load(lib_gtk, "g_log_set_handler", g_log_set_handler)) return false;
if (!load(lib_gtk, "g_log_default_handler", g_log_default_handler)) return false;
if (!load(lib_gtk, "gdk_atom_intern", gdk_atom_intern)) return false;
if (!load(lib_gtk, "gdk_pixbuf_get_has_alpha", gdk_pixbuf_get_has_alpha)) return false;
if (!load(lib_gtk, "gdk_pixbuf_get_pixels", gdk_pixbuf_get_pixels)) return false;
if (!load(lib_gtk, "gdk_pixbuf_get_width", gdk_pixbuf_get_width)) return false;
if (!load(lib_gtk, "gdk_pixbuf_get_height", gdk_pixbuf_get_height)) return false;
if (!load(lib_gtk, "gdk_pixbuf_get_rowstride", gdk_pixbuf_get_rowstride)) return false;
if (load(lib_gtk, "gdk_set_allowed_backends", gdk_set_allowed_backends)) {
// We work only with X11 GDK backend.
// Otherwise we get segfault in Ubuntu 17.04 in gtk_init_check() call.
@ -195,6 +206,10 @@ f_gtk_widget_hide_on_delete gtk_widget_hide_on_delete = nullptr;
f_gtk_widget_destroy gtk_widget_destroy = nullptr;
f_gtk_clipboard_get gtk_clipboard_get = nullptr;
f_gtk_clipboard_store gtk_clipboard_store = nullptr;
f_gtk_clipboard_wait_for_contents gtk_clipboard_wait_for_contents = nullptr;
f_gtk_clipboard_wait_for_image gtk_clipboard_wait_for_image = nullptr;
f_gtk_selection_data_targets_include_image gtk_selection_data_targets_include_image = nullptr;
f_gtk_selection_data_free gtk_selection_data_free = nullptr;
f_gtk_file_chooser_dialog_new gtk_file_chooser_dialog_new = nullptr;
f_gtk_file_chooser_get_type gtk_file_chooser_get_type = nullptr;
f_gtk_image_get_type gtk_image_get_type = nullptr;
@ -234,9 +249,15 @@ f_g_type_check_instance_is_a g_type_check_instance_is_a = nullptr;
f_g_signal_connect_data g_signal_connect_data = nullptr;
f_g_signal_handler_disconnect g_signal_handler_disconnect = nullptr;
f_gdk_init_check gdk_init_check = nullptr;
f_gdk_atom_intern gdk_atom_intern = nullptr;
f_gdk_pixbuf_new_from_data gdk_pixbuf_new_from_data = nullptr;
f_gdk_pixbuf_new_from_file gdk_pixbuf_new_from_file = nullptr;
f_gdk_pixbuf_new_from_file_at_size gdk_pixbuf_new_from_file_at_size = nullptr;
f_gdk_pixbuf_get_has_alpha gdk_pixbuf_get_has_alpha = nullptr;
f_gdk_pixbuf_get_pixels gdk_pixbuf_get_pixels = nullptr;
f_gdk_pixbuf_get_width gdk_pixbuf_get_width = nullptr;
f_gdk_pixbuf_get_height gdk_pixbuf_get_height = nullptr;
f_gdk_pixbuf_get_rowstride gdk_pixbuf_get_rowstride = nullptr;
f_gtk_status_icon_new_from_pixbuf gtk_status_icon_new_from_pixbuf = nullptr;
f_gtk_status_icon_set_from_pixbuf gtk_status_icon_set_from_pixbuf = nullptr;
f_gtk_status_icon_new_from_file gtk_status_icon_new_from_file = nullptr;
@ -265,6 +286,10 @@ f_g_log_default_handler g_log_default_handler = nullptr;
void start() {
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
if (!UseGtkIntegration()) {
return;
}
DEBUG_LOG(("Loading libraries"));
bool gtkLoaded = false;

View File

@ -98,6 +98,18 @@ extern f_gtk_clipboard_get gtk_clipboard_get;
typedef void (*f_gtk_clipboard_store)(GtkClipboard *clipboard);
extern f_gtk_clipboard_store gtk_clipboard_store;
typedef GtkSelectionData* (*f_gtk_clipboard_wait_for_contents)(GtkClipboard *clipboard, GdkAtom target);
extern f_gtk_clipboard_wait_for_contents gtk_clipboard_wait_for_contents;
typedef GdkPixbuf* (*f_gtk_clipboard_wait_for_image)(GtkClipboard *clipboard);
extern f_gtk_clipboard_wait_for_image gtk_clipboard_wait_for_image;
typedef gboolean (*f_gtk_selection_data_targets_include_image)(const GtkSelectionData *selection_data, gboolean writable);
extern f_gtk_selection_data_targets_include_image gtk_selection_data_targets_include_image;
typedef void (*f_gtk_selection_data_free)(GtkSelectionData *data);
extern f_gtk_selection_data_free gtk_selection_data_free;
typedef GtkWidget* (*f_gtk_file_chooser_dialog_new)(const gchar *title, GtkWindow *parent, GtkFileChooserAction action, const gchar *first_button_text, ...) G_GNUC_NULL_TERMINATED;
extern f_gtk_file_chooser_dialog_new gtk_file_chooser_dialog_new;
@ -279,6 +291,9 @@ extern f_g_signal_handler_disconnect g_signal_handler_disconnect;
typedef gboolean (*f_gdk_init_check)(gint *argc, gchar ***argv);
extern f_gdk_init_check gdk_init_check;
typedef GdkAtom (*f_gdk_atom_intern)(const gchar *atom_name, gboolean only_if_exists);
extern f_gdk_atom_intern gdk_atom_intern;
typedef GdkPixbuf* (*f_gdk_pixbuf_new_from_data)(const guchar *data, GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int width, int height, int rowstride, GdkPixbufDestroyNotify destroy_fn, gpointer destroy_fn_data);
extern f_gdk_pixbuf_new_from_data gdk_pixbuf_new_from_data;
@ -288,6 +303,21 @@ extern f_gdk_pixbuf_new_from_file gdk_pixbuf_new_from_file;
typedef GdkPixbuf* (*f_gdk_pixbuf_new_from_file_at_size)(const gchar *filename, int width, int height, GError **error);
extern f_gdk_pixbuf_new_from_file_at_size gdk_pixbuf_new_from_file_at_size;
typedef gboolean (*f_gdk_pixbuf_get_has_alpha)(const GdkPixbuf *pixbuf);
extern f_gdk_pixbuf_get_has_alpha gdk_pixbuf_get_has_alpha;
typedef guchar* (*f_gdk_pixbuf_get_pixels)(const GdkPixbuf *pixbuf);
extern f_gdk_pixbuf_get_pixels gdk_pixbuf_get_pixels;
typedef int (*f_gdk_pixbuf_get_width)(const GdkPixbuf *pixbuf);
extern f_gdk_pixbuf_get_width gdk_pixbuf_get_width;
typedef int (*f_gdk_pixbuf_get_height)(const GdkPixbuf *pixbuf);
extern f_gdk_pixbuf_get_height gdk_pixbuf_get_height;
typedef int (*f_gdk_pixbuf_get_rowstride)(const GdkPixbuf *pixbuf);
extern f_gdk_pixbuf_get_rowstride gdk_pixbuf_get_rowstride;
typedef GtkStatusIcon* (*f_gtk_status_icon_new_from_pixbuf)(GdkPixbuf *pixbuf);
extern f_gtk_status_icon_new_from_pixbuf gtk_status_icon_new_from_pixbuf;

View File

@ -411,6 +411,11 @@ void ForceDisabled(QAction *action, bool disabled) {
MainWindow::MainWindow(not_null<Window::Controller*> controller)
: Window::MainWindow(controller) {
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
if (Libs::gtk_clipboard_get != nullptr) {
_gtkClipboard = Libs::gtk_clipboard_get(Libs::gdk_atom_intern("CLIPBOARD", true));
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
}
void MainWindow::initHook() {

View File

@ -18,6 +18,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <dbusmenuexporter.h>
#endif
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
typedef struct _GtkClipboard GtkClipboard;
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
namespace Platform {
class MainWindow : public Window::MainWindow {
@ -70,6 +75,12 @@ public slots:
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
GtkClipboard *gtkClipboard() {
return _gtkClipboard;
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
protected:
void initHook() override;
void unreadCounterChangedHook() override;
@ -133,6 +144,10 @@ private:
void attachToSNITrayIcon();
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
GtkClipboard *_gtkClipboard = nullptr;
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
};
} // namespace Platform

View File

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/update_checker.h"
#include <QtWidgets/QApplication>
#include <QtWidgets/QStyleFactory>
#include <QtWidgets/QDesktopWidget>
#include <QtCore/QStandardPaths>
#include <QtCore/QProcess>
@ -55,6 +56,8 @@ constexpr auto kXDGDesktopPortalService = "org.freedesktop.portal.Desktop"_cs;
constexpr auto kXDGDesktopPortalObjectPath = "/org/freedesktop/portal/desktop"_cs;
constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs;
QStringList PlatformThemes;
#ifndef TDESKTOP_DISABLE_DBUS_INTEGRATION
void PortalAutostart(bool autostart, bool silent = false) {
QVariantMap options;
@ -295,14 +298,22 @@ bool IsStaticBinary() {
#endif // !DESKTOP_APP_USE_PACKAGED
}
bool UseGtkIntegration() {
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
static const auto Result = !qEnvironmentVariableIsSet(
"TDESKTOP_DISABLE_GTK_INTEGRATION");
return Result;
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
return false;
}
bool IsGtkIntegrationForced() {
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
static const auto Result = [&] {
const auto platformThemes = QString::fromUtf8(qgetenv("QT_QPA_PLATFORMTHEME"))
.split(':', QString::SkipEmptyParts);
return platformThemes.contains(qstr("gtk3"), Qt::CaseInsensitive)
|| platformThemes.contains(qstr("gtk2"), Qt::CaseInsensitive);
return PlatformThemes.contains(qstr("gtk3"), Qt::CaseInsensitive)
|| PlatformThemes.contains(qstr("gtk2"), Qt::CaseInsensitive);
}();
return Result;
@ -510,6 +521,43 @@ QString GetIconName() {
return Result;
}
QImage GetImageFromClipboard() {
QImage data;
#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION
if (!App::wnd()->gtkClipboard()) {
return data;
}
auto gsel = Libs::gtk_clipboard_wait_for_contents(
App::wnd()->gtkClipboard(),
Libs::gdk_atom_intern("TARGETS", true));
if (gsel) {
if (Libs::gtk_selection_data_targets_include_image(gsel, false)) {
auto img = Libs::gtk_clipboard_wait_for_image(App::wnd()->gtkClipboard());
if (img) {
data = QImage(
Libs::gdk_pixbuf_get_pixels(img),
Libs::gdk_pixbuf_get_width(img),
Libs::gdk_pixbuf_get_height(img),
Libs::gdk_pixbuf_get_rowstride(img),
Libs::gdk_pixbuf_get_has_alpha(img)
? QImage::Format_RGBA8888
: QImage::Format_RGB888).copy();
Libs::g_object_unref(img);
}
}
Libs::gtk_selection_data_free(gsel);
}
#endif // !TDESKTOP_DISABLE_GTK_INTEGRATION
return data;
}
std::optional<crl::time> LastUserInputTime() {
// TODO: a fallback pure-X11 implementation, this one covers only major DEs on X11 and Wayland
// an example: https://stackoverflow.com/q/9049087
@ -696,6 +744,9 @@ int psFixPrevious() {
namespace Platform {
void start() {
PlatformThemes = QString::fromUtf8(qgetenv("QT_QPA_PLATFORMTHEME"))
.split(':', QString::SkipEmptyParts);
LOG(("Launcher filename: %1").arg(GetLauncherFilename()));
#ifdef TDESKTOP_USE_FONTCONFIG_FALLBACK
@ -705,6 +756,16 @@ void start() {
qputenv("PULSE_PROP_application.name", AppName.utf8());
qputenv("PULSE_PROP_application.icon_name", GetIconName().toLatin1());
// if gtk integration and qgtk3/qgtk2 platformtheme (or qgtk2 style)
// is used at the same time, the app will crash
if (UseGtkIntegration()
&& !IsStaticBinary()
&& !qEnvironmentVariableIsSet(
"TDESKTOP_I_KNOW_ABOUT_GTK_INCOMPATIBILITY")) {
qunsetenv("QT_QPA_PLATFORMTHEME");
QApplication::setStyle(QStyleFactory::create(qsl("Fusion")));
}
if(IsStaticBinary()
|| InAppImage()
|| InFlatpak()

View File

@ -24,6 +24,7 @@ bool InFlatpak();
bool InSnap();
bool InAppImage();
bool IsStaticBinary();
bool UseGtkIntegration();
bool IsGtkIntegrationForced();
bool UseGtkFileDialog();
bool IsQtPluginsBundled();
@ -34,10 +35,8 @@ bool CanOpenDirectoryWithPortal();
QString ProcessNameByPID(const QString &pid);
QString RealExecutablePath(int argc, char *argv[]);
QString CurrentExecutablePath(int argc, char *argv[]);
QString AppRuntimeDirectory();
QString SingleInstanceLocalServerName(const QString &hash);
QString GetLauncherBasename();
QString GetLauncherFilename();

View File

@ -16,10 +16,6 @@ class LocationPoint;
namespace Platform {
QString CurrentExecutablePath(int argc, char *argv[]);
QString SingleInstanceLocalServerName(const QString &hash);
void RemoveQuarantine(const QString &path);
inline void FallbackFontConfigCheckBegin() {
@ -28,6 +24,10 @@ inline void FallbackFontConfigCheckBegin() {
inline void FallbackFontConfigCheckEnd() {
}
inline QImage GetImageFromClipboard() {
return {};
}
namespace ThirdParty {
inline void start() {

View File

@ -28,6 +28,8 @@ enum class SystemSettingsType {
void SetWatchingMediaKeys(bool watching);
void SetApplicationIcon(const QIcon &icon);
QString CurrentExecutablePath(int argc, char *argv[]);
QString SingleInstanceLocalServerName(const QString &hash);
void RegisterCustomScheme(bool force = false);
PermissionStatus GetPermissionStatus(PermissionType type);
void RequestPermission(PermissionType type, Fn<void(PermissionStatus)> resultCallback);
@ -41,6 +43,7 @@ bool OpenSystemSettings(SystemSettingsType type);
void IgnoreApplicationActivationRightNow();
bool AutostartSupported();
QImage GetImageFromClipboard();
namespace ThirdParty {

View File

@ -19,10 +19,6 @@ namespace Platform {
inline void SetWatchingMediaKeys(bool watching) {
}
QString CurrentExecutablePath(int argc, char *argv[]);
QString SingleInstanceLocalServerName(const QString &hash);
inline void IgnoreApplicationActivationRightNow() {
}
@ -32,6 +28,10 @@ inline void FallbackFontConfigCheckBegin() {
inline void FallbackFontConfigCheckEnd() {
}
inline QImage GetImageFromClipboard() {
return {};
}
namespace ThirdParty {
void start();

View File

@ -8,6 +8,7 @@ option(TDESKTOP_USE_FONTCONFIG_FALLBACK "Use custom fonts.conf (Linux only)." OF
option(TDESKTOP_USE_GTK_FILE_DIALOG "Use custom code for GTK file dialog (Linux only)." OFF)
option(TDESKTOP_DISABLE_REGISTER_CUSTOM_SCHEME "Disable automatic 'tg://' URL scheme handler registration." ${DESKTOP_APP_USE_PACKAGED})
option(TDESKTOP_DISABLE_NETWORK_PROXY "Disable all code for working through Socks5 or MTProxy." OFF)
option(TDESKTOP_DISABLE_GTK_INTEGRATION "Disable all code for GTK integration (Linux only)." OFF)
option(TDESKTOP_USE_PACKAGED_TGVOIP "Find libtgvoip using CMake instead of bundled one." ${DESKTOP_APP_USE_PACKAGED})
option(TDESKTOP_API_TEST "Use test API credentials." OFF)
set(TDESKTOP_API_ID "0" CACHE STRING "Provide 'api_id' for the Telegram API access.")
@ -46,7 +47,6 @@ if (NOT DESKTOP_APP_USE_PACKAGED)
set(TDESKTOP_USE_GTK_FILE_DIALOG ON)
endif()
set(TDESKTOP_DISABLE_GTK_INTEGRATION ON)
if (TDESKTOP_USE_GTK_FILE_DIALOG)
set(TDESKTOP_DISABLE_GTK_INTEGRATION OFF)
endif()