diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index edcb57b56..f91645178 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -80,7 +80,7 @@ auto ListFromMimeData(not_null data) { if (result.error == Error::None) { return result; } else if (data->hasImage()) { - auto image = Platform::GetImageFromClipboard(); + auto image = Platform::GetClipboardImage(); if (image.isNull()) { image = qvariant_cast(data->imageData()); } diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 041ae00e5..bbefbb8bc 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -767,7 +767,7 @@ bool SendFilesBox::addFiles(not_null data) { if (result.error == Ui::PreparedList::Error::None) { return result; } else if (data->hasImage()) { - auto image = Platform::GetImageFromClipboard(); + auto image = Platform::GetClipboardImage(); if (image.isNull()) { image = qvariant_cast(data->imageData()); } diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index bf383ddec..0f9dba8db 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/message_field.h" #include "boxes/sticker_set_box.h" #include "base/platform/base_platform_info.h" +#include "platform/platform_specific.h" #include "mainwindow.h" #include "mainwidget.h" #include "core/application.h" @@ -1219,7 +1220,9 @@ void InnerWidget::copyContextImage(not_null photo) { } const auto image = media->image(Data::PhotoSize::Large)->original(); - QGuiApplication::clipboard()->setImage(image); + if (!Platform::SetClipboardImage(image)) { + QGuiApplication::clipboard()->setImage(image); + } } void InnerWidget::copySelectedText() { diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 9e96faf10..36ed444e4 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -38,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/sticker_set_box.h" #include "chat_helpers/message_field.h" #include "history/history_widget.h" +#include "platform/platform_specific.h" #include "base/platform/base_platform_info.h" #include "base/unixtime.h" #include "mainwindow.h" @@ -1899,7 +1900,9 @@ void HistoryInner::copyContextImage(not_null photo) { } const auto image = media->image(Data::PhotoSize::Large)->original(); - QGuiApplication::clipboard()->setImage(image); + if (!Platform::SetClipboardImage(image)) { + QGuiApplication::clipboard()->setImage(image); + } } void HistoryInner::showStickerPackInfo(not_null document) { diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index fe5c964c0..a03f8e27b 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -4335,7 +4335,7 @@ bool HistoryWidget::confirmSendingFiles( } if (hasImage) { - auto image = Platform::GetImageFromClipboard(); + auto image = Platform::GetClipboardImage(); if (image.isNull()) { image = qvariant_cast(data->imageData()); } diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 961efe7d9..815805c1b 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_scheduled_messages.h" #include "core/file_utilities.h" #include "base/platform/base_platform_info.h" +#include "platform/platform_specific.h" #include "window/window_peer_menu.h" #include "window/window_session_controller.h" #include "lang/lang_keys.h" @@ -125,7 +126,9 @@ void CopyImage(not_null photo) { } const auto image = media->image(Data::PhotoSize::Large)->original(); - QGuiApplication::clipboard()->setImage(image); + if (!Platform::SetClipboardImage(image)) { + QGuiApplication::clipboard()->setImage(image); + } } void ShowStickerPackInfo( diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 1787f399b..2551912cf 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -603,7 +603,7 @@ bool RepliesWidget::confirmSendingFiles( } if (hasImage) { - auto image = Platform::GetImageFromClipboard(); + auto image = Platform::GetClipboardImage(); if (image.isNull()) { image = qvariant_cast(data->imageData()); } diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 484fbd69b..48098ec80 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -335,7 +335,7 @@ bool ScheduledWidget::confirmSendingFiles( } if (hasImage) { - auto image = Platform::GetImageFromClipboard(); + auto image = Platform::GetClipboardImage(); if (image.isNull()) { image = qvariant_cast(data->imageData()); } diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index d6c8fc141..abea774de 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -50,6 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_peer_menu.h" #include "window/window_session_controller.h" #include "window/window_controller.h" +#include "platform/platform_specific.h" #include "base/platform/base_platform_info.h" #include "base/unixtime.h" #include "main/main_account.h" @@ -1652,13 +1653,18 @@ void OverlayWidget::onOverview() { void OverlayWidget::onCopy() { _dropdown->hideAnimated(Ui::DropdownMenu::HideOption::IgnoreShow); if (_document) { - QGuiApplication::clipboard()->setImage(videoShown() + const auto image = videoShown() ? transformVideoFrame(videoFrame()) - : transformStaticContent(_staticContent)); + : transformStaticContent(_staticContent); + if (!Platform::SetClipboardImage(image)) { + QGuiApplication::clipboard()->setImage(image); + } } else if (_photo && _photoMedia->loaded()) { const auto image = _photoMedia->image( Data::PhotoSize::Large)->original(); - QGuiApplication::clipboard()->setImage(image); + if (!Platform::SetClipboardImage(image)) { + QGuiApplication::clipboard()->setImage(image); + } } } diff --git a/Telegram/SourceFiles/platform/linux/linux_libs.cpp b/Telegram/SourceFiles/platform/linux/linux_libs.cpp index 793b811d7..4cfba56ea 100644 --- a/Telegram/SourceFiles/platform/linux/linux_libs.cpp +++ b/Telegram/SourceFiles/platform/linux/linux_libs.cpp @@ -73,10 +73,6 @@ bool setupGtkBase(QLibrary &lib_gtk) { if (!LOAD_SYMBOL(lib_gtk, "gtk_widget_get_type", gtk_widget_get_type)) return false; if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_get", gtk_clipboard_get)) return false; if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_store", gtk_clipboard_store)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_image", gtk_clipboard_wait_for_image)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_selection_data_targets_include_image", gtk_selection_data_targets_include_image)) return false; - if (!LOAD_SYMBOL(lib_gtk, "gtk_selection_data_free", gtk_selection_data_free)) return false; if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_dialog_new", gtk_file_chooser_dialog_new)) return false; if (!LOAD_SYMBOL(lib_gtk, "gtk_file_chooser_get_type", gtk_file_chooser_get_type)) return false; if (!LOAD_SYMBOL(lib_gtk, "gtk_image_get_type", gtk_image_get_type)) return false; @@ -215,6 +211,7 @@ f_gtk_widget_destroy gtk_widget_destroy = nullptr; f_gtk_widget_get_type gtk_widget_get_type = nullptr; f_gtk_clipboard_get gtk_clipboard_get = nullptr; f_gtk_clipboard_store gtk_clipboard_store = nullptr; +f_gtk_clipboard_set_image gtk_clipboard_set_image = 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; @@ -257,6 +254,7 @@ f_gdk_window_focus gdk_window_focus = nullptr; f_gtk_dialog_get_type gtk_dialog_get_type = nullptr; f_gtk_dialog_run gtk_dialog_run = 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_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; @@ -296,6 +294,7 @@ void start() { } if (gtkLoaded) { + LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_new_from_data", gdk_pixbuf_new_from_data); LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_new_from_file_at_size", gdk_pixbuf_new_from_file_at_size); LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_get_has_alpha", gdk_pixbuf_get_has_alpha); LOAD_SYMBOL(lib_gtk, "gdk_pixbuf_get_pixels", gdk_pixbuf_get_pixels); @@ -305,6 +304,12 @@ void start() { internal::GdkHelperLoad(lib_gtk); + LOAD_SYMBOL(lib_gtk, "gtk_clipboard_set_image", gtk_clipboard_set_image); + LOAD_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents); + LOAD_SYMBOL(lib_gtk, "gtk_clipboard_wait_for_image", gtk_clipboard_wait_for_image); + LOAD_SYMBOL(lib_gtk, "gtk_selection_data_targets_include_image", gtk_selection_data_targets_include_image); + LOAD_SYMBOL(lib_gtk, "gtk_selection_data_free", gtk_selection_data_free); + LOAD_SYMBOL(lib_gtk, "gtk_dialog_get_widget_for_response", gtk_dialog_get_widget_for_response); LOAD_SYMBOL(lib_gtk, "gtk_button_set_label", gtk_button_set_label); LOAD_SYMBOL(lib_gtk, "gtk_button_get_type", gtk_button_get_type); diff --git a/Telegram/SourceFiles/platform/linux/linux_libs.h b/Telegram/SourceFiles/platform/linux/linux_libs.h index fa651615d..543019b7d 100644 --- a/Telegram/SourceFiles/platform/linux/linux_libs.h +++ b/Telegram/SourceFiles/platform/linux/linux_libs.h @@ -88,6 +88,9 @@ 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 void (*f_gtk_clipboard_set_image)(::GtkClipboard *clipboard, GdkPixbuf *pixbuf); +extern f_gtk_clipboard_set_image gtk_clipboard_set_image; + typedef GtkSelectionData* (*f_gtk_clipboard_wait_for_contents)(::GtkClipboard *clipboard, GdkAtom target); extern f_gtk_clipboard_wait_for_contents gtk_clipboard_wait_for_contents; @@ -268,6 +271,9 @@ extern f_gtk_dialog_run gtk_dialog_run; 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; + 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; diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 2f9f3d35b..69e9f21b6 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -325,7 +325,7 @@ bool GenerateDesktopFile( } #ifndef TDESKTOP_DISABLE_GTK_INTEGRATION -bool GetImageFromClipboardSupported() { +bool GetClipboardImageSupported() { return (Libs::gtk_clipboard_wait_for_contents != nullptr) && (Libs::gtk_clipboard_wait_for_image != nullptr) && (Libs::gtk_selection_data_targets_include_image != nullptr) @@ -337,6 +337,11 @@ bool GetImageFromClipboardSupported() { && (Libs::gdk_pixbuf_get_has_alpha != nullptr) && (Libs::gdk_atom_intern != nullptr); } + +bool SetClipboardImageSupported() { + return (Libs::gtk_clipboard_set_image != nullptr) + && (Libs::gdk_pixbuf_new_from_data != nullptr); +} #endif // !TDESKTOP_DISABLE_GTK_INTEGRATION uint XCBMoveResizeFromEdges(Qt::Edges edges) { @@ -729,11 +734,11 @@ QString GetIconName() { return Result; } -QImage GetImageFromClipboard() { +QImage GetClipboardImage() { QImage data; #ifndef TDESKTOP_DISABLE_GTK_INTEGRATION - if (!GetImageFromClipboardSupported() || !Libs::GtkClipboard()) { + if (!GetClipboardImageSupported() || !Libs::GtkClipboard()) { return data; } @@ -767,6 +772,57 @@ QImage GetImageFromClipboard() { return data; } +bool SetClipboardImage(const QImage &image) { +#ifndef TDESKTOP_DISABLE_GTK_INTEGRATION + if (!SetClipboardImageSupported() + || !Libs::GtkClipboard() + || image.isNull()) { + return false; + } + + const auto convertedImage = [&] { + if (image.hasAlphaChannel()) { + if (image.format() != QImage::Format_RGBA8888) { + return image.convertToFormat(QImage::Format_RGBA8888); + } + } else { + if (image.format() != QImage::Format_RGB888) { + return image.convertToFormat(QImage::Format_RGB888); + } + } + return image; + }(); + + auto imageBuf = reinterpret_cast( + malloc(convertedImage.sizeInBytes())); + + memcpy( + imageBuf, + convertedImage.constBits(), + convertedImage.sizeInBytes()); + + auto imagePixbuf = Libs::gdk_pixbuf_new_from_data( + imageBuf, + GDK_COLORSPACE_RGB, + convertedImage.hasAlphaChannel(), + 8, + convertedImage.width(), + convertedImage.height(), + convertedImage.bytesPerLine(), + [](guchar *pixels, gpointer data) { + free(pixels); + }, + nullptr); + + Libs::gtk_clipboard_set_image(Libs::GtkClipboard(), imagePixbuf); + g_object_unref(imagePixbuf); + + return true; +#else // !TDESKTOP_DISABLE_GTK_INTEGRATION + return false; +#endif // TDESKTOP_DISABLE_GTK_INTEGRATION +} + std::optional IsDarkMode() { #ifndef TDESKTOP_DISABLE_GTK_INTEGRATION if (Libs::GtkSettingSupported() && Libs::GtkLoaded()) { diff --git a/Telegram/SourceFiles/platform/mac/specific_mac.h b/Telegram/SourceFiles/platform/mac/specific_mac.h index 00b0bb44c..5ad6335fb 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac.h +++ b/Telegram/SourceFiles/platform/mac/specific_mac.h @@ -18,10 +18,14 @@ namespace Platform { [[nodiscard]] bool IsDarkMenuBar(); -inline QImage GetImageFromClipboard() { +inline QImage GetClipboardImage() { return {}; } +inline bool SetClipboardImage(const QImage &image) { + return false; +} + inline bool StartSystemMove(QWindow *window) { return false; } diff --git a/Telegram/SourceFiles/platform/platform_specific.h b/Telegram/SourceFiles/platform/platform_specific.h index 45fafaefa..71cb05b67 100644 --- a/Telegram/SourceFiles/platform/platform_specific.h +++ b/Telegram/SourceFiles/platform/platform_specific.h @@ -41,7 +41,8 @@ void IgnoreApplicationActivationRightNow(); bool AutostartSupported(); bool TrayIconSupported(); bool SkipTaskbarSupported(); -QImage GetImageFromClipboard(); +QImage GetClipboardImage(); +bool SetClipboardImage(const QImage &image); bool StartSystemMove(QWindow *window); bool StartSystemResize(QWindow *window, Qt::Edges edges); bool ShowWindowMenu(QWindow *window); diff --git a/Telegram/SourceFiles/platform/win/specific_win.h b/Telegram/SourceFiles/platform/win/specific_win.h index c5fa006a5..98fd7adc2 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.h +++ b/Telegram/SourceFiles/platform/win/specific_win.h @@ -22,10 +22,14 @@ inline void SetWatchingMediaKeys(bool watching) { inline void IgnoreApplicationActivationRightNow() { } -inline QImage GetImageFromClipboard() { +inline QImage GetClipboardImage() { return {}; } +inline bool SetClipboardImage(const QImage &image) { + return false; +} + inline bool StartSystemMove(QWindow *window) { return false; }