/* 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_gtk_integration.h" #include "base/platform/linux/base_linux_gtk_integration.h" #include "base/platform/linux/base_linux_gtk_integration_p.h" #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/specific_linux.h" #include "ui/platform/ui_platform_utility.h" #include "core/sandbox.h" #include "core/core_settings.h" #include "core/application.h" namespace Platform { namespace internal { using namespace Platform::Gtk; using BaseGtkIntegration = base::Platform::GtkIntegration; namespace { bool Loaded = false; QLibrary &Library() { return BaseGtkIntegration::Instance()->library(); } bool GetImageFromClipboardSupported() { return (gtk_clipboard_get != nullptr) && (gtk_clipboard_wait_for_contents != nullptr) && (gtk_clipboard_wait_for_image != nullptr) && (gtk_selection_data_targets_include_image != nullptr) && (gtk_selection_data_free != nullptr) && (gdk_pixbuf_get_pixels != nullptr) && (gdk_pixbuf_get_width != nullptr) && (gdk_pixbuf_get_height != nullptr) && (gdk_pixbuf_get_rowstride != nullptr) && (gdk_pixbuf_get_has_alpha != nullptr) && (gdk_atom_intern != nullptr); } void SetScaleFactor() { Core::Sandbox::Instance().customEnterFromEventLoop([] { const auto integration = GtkIntegration::Instance(); const auto ratio = Core::Sandbox::Instance().devicePixelRatio(); if (!integration || ratio > 1.) { return; } const auto scaleFactor = integration->scaleFactor().value_or(1); if (scaleFactor == 1) { return; } LOG(("GTK scale factor: %1").arg(scaleFactor)); cSetScreenScale(style::CheckScale(scaleFactor * 100)); }); } void DarkModeChanged() { Core::Sandbox::Instance().customEnterFromEventLoop([] { Core::App().settings().setSystemDarkMode(IsDarkMode()); }); } } // namespace GtkIntegration::GtkIntegration() { } GtkIntegration *GtkIntegration::Instance() { if (!BaseGtkIntegration::Instance()) { return nullptr; } static GtkIntegration instance; return &instance; } void GtkIntegration::load() { Expects(!loaded()); if (!BaseGtkIntegration::Instance()->loaded()) { return; } LOAD_GTK_SYMBOL(Library(), "gtk_widget_show", gtk_widget_show); LOAD_GTK_SYMBOL(Library(), "gtk_widget_hide", gtk_widget_hide); LOAD_GTK_SYMBOL(Library(), "gtk_widget_get_window", gtk_widget_get_window); LOAD_GTK_SYMBOL(Library(), "gtk_widget_realize", gtk_widget_realize); LOAD_GTK_SYMBOL(Library(), "gtk_widget_hide_on_delete", gtk_widget_hide_on_delete); LOAD_GTK_SYMBOL(Library(), "gtk_widget_destroy", gtk_widget_destroy); LOAD_GTK_SYMBOL(Library(), "gtk_clipboard_get", gtk_clipboard_get); LOAD_GTK_SYMBOL(Library(), "gtk_clipboard_store", gtk_clipboard_store); LOAD_GTK_SYMBOL(Library(), "gtk_clipboard_wait_for_contents", gtk_clipboard_wait_for_contents); LOAD_GTK_SYMBOL(Library(), "gtk_clipboard_wait_for_image", gtk_clipboard_wait_for_image); LOAD_GTK_SYMBOL(Library(), "gtk_selection_data_targets_include_image", gtk_selection_data_targets_include_image); LOAD_GTK_SYMBOL(Library(), "gtk_selection_data_free", gtk_selection_data_free); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_dialog_new", gtk_file_chooser_dialog_new); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_type", gtk_file_chooser_get_type); LOAD_GTK_SYMBOL(Library(), "gtk_image_get_type", gtk_image_get_type); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_current_folder", gtk_file_chooser_set_current_folder); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_current_folder", gtk_file_chooser_get_current_folder); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_current_name", gtk_file_chooser_set_current_name); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_select_filename", gtk_file_chooser_select_filename); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_filenames", gtk_file_chooser_get_filenames); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_filter", gtk_file_chooser_set_filter); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_filter", gtk_file_chooser_get_filter); LOAD_GTK_SYMBOL(Library(), "gtk_window_get_type", gtk_window_get_type); LOAD_GTK_SYMBOL(Library(), "gtk_window_set_title", gtk_window_set_title); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_local_only", gtk_file_chooser_set_local_only); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_action", gtk_file_chooser_set_action); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_select_multiple", gtk_file_chooser_set_select_multiple); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_do_overwrite_confirmation", gtk_file_chooser_set_do_overwrite_confirmation); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_remove_filter", gtk_file_chooser_remove_filter); LOAD_GTK_SYMBOL(Library(), "gtk_file_filter_set_name", gtk_file_filter_set_name); LOAD_GTK_SYMBOL(Library(), "gtk_file_filter_add_pattern", gtk_file_filter_add_pattern); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_add_filter", gtk_file_chooser_add_filter); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_preview_widget", gtk_file_chooser_set_preview_widget); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_get_preview_filename", gtk_file_chooser_get_preview_filename); LOAD_GTK_SYMBOL(Library(), "gtk_file_chooser_set_preview_widget_active", gtk_file_chooser_set_preview_widget_active); LOAD_GTK_SYMBOL(Library(), "gtk_file_filter_new", gtk_file_filter_new); LOAD_GTK_SYMBOL(Library(), "gtk_image_new", gtk_image_new); LOAD_GTK_SYMBOL(Library(), "gtk_image_set_from_pixbuf", gtk_image_set_from_pixbuf); LOAD_GTK_SYMBOL(Library(), "gdk_window_set_modal_hint", gdk_window_set_modal_hint); LOAD_GTK_SYMBOL(Library(), "gdk_window_focus", gdk_window_focus); LOAD_GTK_SYMBOL(Library(), "gtk_dialog_get_type", gtk_dialog_get_type); LOAD_GTK_SYMBOL(Library(), "gtk_dialog_run", gtk_dialog_run); LOAD_GTK_SYMBOL(Library(), "gdk_atom_intern", gdk_atom_intern); LOAD_GTK_SYMBOL(Library(), "gdk_display_get_default", gdk_display_get_default); LOAD_GTK_SYMBOL(Library(), "gdk_display_get_monitor", gdk_display_get_monitor); LOAD_GTK_SYMBOL(Library(), "gdk_display_get_primary_monitor", gdk_display_get_primary_monitor); LOAD_GTK_SYMBOL(Library(), "gdk_monitor_get_scale_factor", gdk_monitor_get_scale_factor); LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_new_from_file_at_size", gdk_pixbuf_new_from_file_at_size); LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_has_alpha", gdk_pixbuf_get_has_alpha); LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_pixels", gdk_pixbuf_get_pixels); LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_width", gdk_pixbuf_get_width); LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_height", gdk_pixbuf_get_height); LOAD_GTK_SYMBOL(Library(), "gdk_pixbuf_get_rowstride", gdk_pixbuf_get_rowstride); GdkHelperLoad(Library()); LOAD_GTK_SYMBOL(Library(), "gtk_dialog_get_widget_for_response", gtk_dialog_get_widget_for_response); LOAD_GTK_SYMBOL(Library(), "gtk_button_set_label", gtk_button_set_label); LOAD_GTK_SYMBOL(Library(), "gtk_button_get_type", gtk_button_get_type); LOAD_GTK_SYMBOL(Library(), "gtk_app_chooser_dialog_new", gtk_app_chooser_dialog_new); LOAD_GTK_SYMBOL(Library(), "gtk_app_chooser_get_app_info", gtk_app_chooser_get_app_info); LOAD_GTK_SYMBOL(Library(), "gtk_app_chooser_get_type", gtk_app_chooser_get_type); Loaded = true; SetScaleFactor(); BaseGtkIntegration::Instance()->connectToSetting( "gtk-theme-name", DarkModeChanged); if (BaseGtkIntegration::Instance()->checkVersion(3, 0, 0)) { BaseGtkIntegration::Instance()->connectToSetting( "gtk-application-prefer-dark-theme", DarkModeChanged); } if (BaseGtkIntegration::Instance()->checkVersion(3, 12, 0)) { BaseGtkIntegration::Instance()->connectToSetting( "gtk-decoration-layout", Ui::Platform::NotifyTitleControlsLayoutChanged); } } bool GtkIntegration::loaded() const { return Loaded; } std::optional GtkIntegration::scaleFactor() const { if (!loaded() || (gdk_display_get_default == nullptr) || (gdk_display_get_monitor == nullptr) || (gdk_display_get_primary_monitor == nullptr) || (gdk_monitor_get_scale_factor == nullptr)) { return std::nullopt; } const auto display = gdk_display_get_default(); if (!display) { return std::nullopt; } const auto monitor = [&] { if (const auto primary = gdk_display_get_primary_monitor(display)) { return primary; } return gdk_display_get_monitor(display, 0); }(); if (!monitor) { return std::nullopt; } return gdk_monitor_get_scale_factor(monitor); } bool GtkIntegration::fileDialogSupported() const { return FileDialog::Gtk::Supported(); } bool GtkIntegration::useFileDialog(FileDialogType type) const { return FileDialog::Gtk::Use(type); } bool GtkIntegration::getFileDialog( QPointer parent, QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, FileDialogType type, QString startFile) const { return FileDialog::Gtk::Get( parent, files, remoteContent, caption, filter, type, startFile); } bool GtkIntegration::showOpenWithDialog(const QString &filepath) const { return File::internal::ShowOpenWithDialog(filepath); } QImage GtkIntegration::getImageFromClipboard() const { QImage data; if (!GetImageFromClipboardSupported()) { return data; } const auto clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); if (!clipboard) { return data; } auto gsel = gtk_clipboard_wait_for_contents( clipboard, gdk_atom_intern("TARGETS", true)); if (gsel) { if (gtk_selection_data_targets_include_image(gsel, false)) { auto img = gtk_clipboard_wait_for_image(clipboard); if (img) { data = QImage( gdk_pixbuf_get_pixels(img), gdk_pixbuf_get_width(img), gdk_pixbuf_get_height(img), gdk_pixbuf_get_rowstride(img), gdk_pixbuf_get_has_alpha(img) ? QImage::Format_RGBA8888 : QImage::Format_RGB888).copy(); g_object_unref(img); } } gtk_selection_data_free(gsel); } return data; } } // namespace internal } // namespace Platform