From 32e47c24b4e7a48fb15b12b955d926c0e4844c05 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 8 Jun 2021 23:30:59 +0300 Subject: [PATCH] Moved Linux MPRIS support to lib_base. --- Telegram/CMakeLists.txt | 2 - .../platform/linux/linux_mpris_support.cpp | 518 ------------------ .../platform/linux/linux_mpris_support.h | 24 - .../platform/linux/specific_linux.cpp | 10 - 4 files changed, 554 deletions(-) delete mode 100644 Telegram/SourceFiles/platform/linux/linux_mpris_support.cpp delete mode 100644 Telegram/SourceFiles/platform/linux/linux_mpris_support.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 07a3995832..93e09681fa 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -869,8 +869,6 @@ PRIVATE 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_mpris_support.cpp - platform/linux/linux_mpris_support.h platform/linux/linux_notification_service_watcher.cpp platform/linux/linux_notification_service_watcher.h platform/linux/linux_wayland_integration_dummy.cpp diff --git a/Telegram/SourceFiles/platform/linux/linux_mpris_support.cpp b/Telegram/SourceFiles/platform/linux/linux_mpris_support.cpp deleted file mode 100644 index 59031d38db..0000000000 --- a/Telegram/SourceFiles/platform/linux/linux_mpris_support.cpp +++ /dev/null @@ -1,518 +0,0 @@ -/* -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_mpris_support.h" - -#include "base/platform/base_platform_info.h" -#include "base/platform/linux/base_linux_glibmm_helper.h" -#include "media/audio/media_audio.h" -#include "media/player/media_player_instance.h" -#include "data/data_file_origin.h" -#include "data/data_document.h" -#include "data/data_document_media.h" -#include "core/sandbox.h" -#include "core/application.h" -#include "core/core_settings.h" -#include "main/main_domain.h" -#include "main/main_account.h" -#include "main/main_session.h" -#include "mainwindow.h" -#include "mainwidget.h" -#include "ui/text/format_song_document_name.h" - -#include -#include - -#include -#include - -namespace Platform { -namespace internal { -namespace { - -constexpr auto kService = "org.mpris.MediaPlayer2.tdesktop"_cs; -constexpr auto kObjectPath = "/org/mpris/MediaPlayer2"_cs; -constexpr auto kFakeTrackPath = "/org/telegram/desktop/track/0"_cs; -constexpr auto kInterface = "org.mpris.MediaPlayer2"_cs; -constexpr auto kPlayerInterface = "org.mpris.MediaPlayer2.Player"_cs; -constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"_cs; - -constexpr auto kIntrospectionXML = R"INTROSPECTION( - - - - - - - - - - - - - -)INTROSPECTION"_cs; - -constexpr auto kPlayerIntrospectionXML = R"INTROSPECTION( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -)INTROSPECTION"_cs; - -auto CreateMetadata( - const Media::Player::TrackState &state, - Data::DocumentMedia *trackView) { - std::map result; - - if (Media::Player::IsStoppedOrStopping(state.state)) { - return result; - } - - result["mpris:trackid"] = Glib::wrap(g_variant_new_object_path( - kFakeTrackPath.utf8().constData())); - result["mpris:length"] = Glib::Variant::create( - state.length * 1000); - - const auto audioData = state.id.audio() - ? state.id.audio() - : trackView - ? trackView->owner().get() - : nullptr; - if (audioData) { - const auto &[title, performer] = - Ui::Text::FormatSongNameFor(audioData).composedName(); - result["xesam:title"] = Glib::Variant< - Glib::ustring - >::create(title.toStdString()); - result["xesam:artist"] = Glib::Variant< - std::vector - >::create({ performer.toStdString() }); - } - - if (trackView) { - trackView->thumbnailWanted(Data::FileOrigin()); - if (trackView->thumbnail()) { - QByteArray thumbnailData; - QBuffer thumbnailBuffer(&thumbnailData); - trackView->thumbnail()->original().save( - &thumbnailBuffer, - "JPG", - 87); - - result["mpris:artUrl"] = Glib::Variant< - Glib::ustring - >::create("data:image/jpeg;base64," - + thumbnailData - .toBase64() - .toStdString()); - } - } - - return result; -} - -auto PlaybackStatus(Media::Player::State state) { - return (state == Media::Player::State::Playing) - ? "Playing" - : Media::Player::IsPausedOrPausing(state) - ? "Paused" - : "Stopped"; -} - -void HandleMethodCall( - const Glib::RefPtr &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 &invocation) { - Core::Sandbox::Instance().customEnterFromEventLoop([&] { - try { - auto parametersCopy = parameters; - - if (method_name == "Quit") { - if (const auto main = App::main()) { - main->closeBothPlayers(); - } - } else if (method_name == "Raise") { - if (const auto window = App::wnd()) { - window->showFromTray(); - } - } else if (method_name == "Next") { - Media::Player::instance()->next(); - } else if (method_name == "Pause") { - Media::Player::instance()->pause(); - } else if (method_name == "Play") { - Media::Player::instance()->play(); - } else if (method_name == "PlayPause") { - Media::Player::instance()->playPause(); - } else if (method_name == "Previous") { - Media::Player::instance()->previous(); - } else if (method_name == "Seek") { - const auto offset = base::Platform::GlibVariantCast( - parametersCopy.get_child(0)); - - const auto state = Media::Player::instance()->getState( - Media::Player::instance()->getActiveType()); - - Media::Player::instance()->finishSeeking( - Media::Player::instance()->getActiveType(), - float64(state.position * 1000 + offset) - / (state.length * 1000)); - } else if (method_name == "SetPosition") { - const auto position = base::Platform::GlibVariantCast( - parametersCopy.get_child(1)); - - const auto state = Media::Player::instance()->getState( - Media::Player::instance()->getActiveType()); - - Media::Player::instance()->finishSeeking( - Media::Player::instance()->getActiveType(), - float64(position) / (state.length * 1000)); - } else if (method_name == "Stop") { - Media::Player::instance()->stop(); - } else { - return; - } - - invocation->return_value({}); - } catch (...) { - } - }); -} - -void HandleGetProperty( - Glib::VariantBase &property, - const Glib::RefPtr &connection, - const Glib::ustring &sender, - const Glib::ustring &object_path, - const Glib::ustring &interface_name, - const Glib::ustring &property_name) { - Core::Sandbox::Instance().customEnterFromEventLoop([&] { - if (property_name == "CanQuit") { - property = Glib::Variant::create(true); - } else if (property_name == "CanRaise") { - property = Glib::Variant::create(!IsWayland()); - } else if (property_name == "CanSetFullscreen") { - property = Glib::Variant::create(false); - } else if (property_name == "DesktopEntry") { - property = Glib::Variant::create( - QGuiApplication::desktopFileName().chopped(8).toStdString()); - } else if (property_name == "Fullscreen") { - property = Glib::Variant::create(false); - } else if (property_name == "HasTrackList") { - property = Glib::Variant::create(false); - } else if (property_name == "Identity") { - property = Glib::Variant::create( - std::string(AppName)); - } else if (property_name == "SupportedMimeTypes") { - property = Glib::Variant>::create({}); - } else if (property_name == "SupportedUriSchemes") { - property = Glib::Variant>::create({}); - } else if (property_name == "CanControl") { - property = Glib::Variant::create(true); - } else if (property_name == "CanGoNext") { - property = Glib::Variant::create(true); - } else if (property_name == "CanGoPrevious") { - property = Glib::Variant::create(true); - } else if (property_name == "CanPause") { - property = Glib::Variant::create(true); - } else if (property_name == "CanPlay") { - property = Glib::Variant::create(true); - } else if (property_name == "CanSeek") { - property = Glib::Variant::create(true); - } else if (property_name == "MaximumRate") { - property = Glib::Variant::create(1.0); - } else if (property_name == "Metadata") { - const auto state = Media::Player::instance()->getState( - Media::Player::instance()->getActiveType()); - - const auto trackView = [&]() -> std::shared_ptr { - const auto audioData = state.id.audio(); - if (audioData && audioData->isSongWithCover()) { - return audioData->activeMediaView(); - } - return nullptr; - }(); - - property = base::Platform::MakeGlibVariant( - CreateMetadata(state, trackView.get())); - } else if (property_name == "MinimumRate") { - property = Glib::Variant::create(1.0); - } else if (property_name == "PlaybackStatus") { - const auto state = Media::Player::instance()->getState( - Media::Player::instance()->getActiveType()); - - property = Glib::Variant::create( - PlaybackStatus(state.state)); - } else if (property_name == "Position") { - const auto state = Media::Player::instance()->getState( - Media::Player::instance()->getActiveType()); - - property = Glib::Variant::create(state.position * 1000); - } else if (property_name == "Rate") { - property = Glib::Variant::create(1.0); - } else if (property_name == "Volume") { - property = Glib::Variant::create( - Core::App().settings().songVolume()); - } - }); -} - -bool HandleSetProperty( - const Glib::RefPtr &connection, - const Glib::ustring &sender, - const Glib::ustring &object_path, - const Glib::ustring &interface_name, - const Glib::ustring &property_name, - const Glib::VariantBase &value) { - try { - if (property_name == "Fullscreen") { - } else if (property_name == "Rate") { - } else if (property_name == "Volume") { - Core::Sandbox::Instance().customEnterFromEventLoop([&] { - Core::App().settings().setSongVolume( - base::Platform::GlibVariantCast(value)); - }); - } else { - return false; - } - - return true; - } catch (...) { - } - - return false; -} - -const Gio::DBus::InterfaceVTable InterfaceVTable( - sigc::ptr_fun(&HandleMethodCall), - sigc::ptr_fun(&HandleGetProperty), - sigc::ptr_fun(&HandleSetProperty)); - -void PlayerPropertyChanged( - const Glib::ustring &name, - const Glib::VariantBase &value) { - try { - const auto connection = Gio::DBus::Connection::get_sync( - Gio::DBus::BusType::BUS_TYPE_SESSION); - - connection->emit_signal( - std::string(kObjectPath), - std::string(kPropertiesInterface), - "PropertiesChanged", - {}, - base::Platform::MakeGlibVariant(std::tuple{ - Glib::ustring(std::string(kPlayerInterface)), - std::map{ - { name, value }, - }, - std::vector{}, - })); - } catch (...) { - } -} - -void Seeked(gint64 position) { - try { - const auto connection = Gio::DBus::Connection::get_sync( - Gio::DBus::BusType::BUS_TYPE_SESSION); - - connection->emit_signal( - std::string(kObjectPath), - std::string(kPlayerInterface), - "Seeked", - {}, - base::Platform::MakeGlibVariant(std::tuple{ - position, - })); - } catch (...) { - } -} - -} // namespace - -class MPRISSupport::Private { -public: - void updateTrackState(const Media::Player::TrackState &state); - - Glib::RefPtr dbusConnection; - Glib::RefPtr introspectionData; - Glib::RefPtr playerIntrospectionData; - - uint ownId = 0; - uint registerId = 0; - uint playerRegisterId = 0; - - Glib::ustring playbackStatus; - gint64 position = 0; - - DocumentData *audioData = nullptr; - std::shared_ptr trackView; - Image *thumbnail = nullptr; - - rpl::lifetime lifetime; -}; - -void MPRISSupport::Private::updateTrackState( - const Media::Player::TrackState &state) { - const auto currentAudioData = state.id.audio(); - const auto currentPosition = state.position * 1000; - const auto currentPlaybackStatus = PlaybackStatus(state.state); - - if (currentAudioData != audioData) { - audioData = currentAudioData; - if (audioData && audioData->isSongWithCover()) { - trackView = audioData->createMediaView(); - thumbnail = trackView->thumbnail(); - } else { - trackView = nullptr; - thumbnail = nullptr; - } - - PlayerPropertyChanged( - "Metadata", - Glib::Variant< - std::map - >::create(CreateMetadata(state, trackView.get()))); - } - - if (trackView && (trackView->thumbnail() != thumbnail)) { - thumbnail = trackView->thumbnail(); - PlayerPropertyChanged( - "Metadata", - Glib::Variant< - std::map - >::create(CreateMetadata(state, trackView.get()))); - } - - if (currentPlaybackStatus != playbackStatus) { - playbackStatus = currentPlaybackStatus; - PlayerPropertyChanged( - "PlaybackStatus", - Glib::Variant::create(playbackStatus)); - } - - if (currentPosition != position) { - const auto positionDifference = position - currentPosition; - if (positionDifference > 1000000 || positionDifference < -1000000) { - Seeked(currentPosition); - } - - position = currentPosition; - } -} - -MPRISSupport::MPRISSupport() -: _private(std::make_unique()) { - try { - _private->introspectionData = Gio::DBus::NodeInfo::create_for_xml( - std::string(kIntrospectionXML)); - - _private->playerIntrospectionData = Gio::DBus::NodeInfo::create_for_xml( - std::string(kPlayerIntrospectionXML)); - - _private->ownId = Gio::DBus::own_name( - Gio::DBus::BusType::BUS_TYPE_SESSION, - std::string(kService)); - - _private->dbusConnection = Gio::DBus::Connection::get_sync( - Gio::DBus::BusType::BUS_TYPE_SESSION); - - _private->registerId = _private->dbusConnection->register_object( - std::string(kObjectPath), - _private->introspectionData->lookup_interface(), - InterfaceVTable); - - _private->playerRegisterId = _private->dbusConnection->register_object( - std::string(kObjectPath), - _private->playerIntrospectionData->lookup_interface(), - InterfaceVTable); - - _private->updateTrackState( - Media::Player::instance()->getState( - Media::Player::instance()->getActiveType())); - - Core::App().domain().active().session().downloaderTaskFinished( - ) | rpl::start_with_next([=] { - _private->updateTrackState( - Media::Player::instance()->getState( - Media::Player::instance()->getActiveType())); - }, _private->lifetime); - - Media::Player::instance()->updatedNotifier( - ) | rpl::filter([=](const Media::Player::TrackState &state) { - return state.id.type() == Media::Player::instance()->getActiveType(); - }) | rpl::start_with_next([=]( - const Media::Player::TrackState &state) { - _private->updateTrackState(state); - }, _private->lifetime); - - Core::App().settings().songVolumeChanges( - ) | rpl::start_with_next([=](float64 volume) { - PlayerPropertyChanged( - "Volume", - Glib::Variant::create(volume)); - }, _private->lifetime); - } catch (...) { - } -} - -MPRISSupport::~MPRISSupport() { - if (_private->dbusConnection) { - if (_private->playerRegisterId) { - _private->dbusConnection->unregister_object( - _private->playerRegisterId); - } - - if (_private->registerId) { - _private->dbusConnection->unregister_object( - _private->registerId); - } - } - - if (_private->ownId) { - Gio::DBus::unown_name(_private->ownId); - } -} - -} // namespace internal -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_mpris_support.h b/Telegram/SourceFiles/platform/linux/linux_mpris_support.h deleted file mode 100644 index 850e35fbc6..0000000000 --- a/Telegram/SourceFiles/platform/linux/linux_mpris_support.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -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 internal { - -class MPRISSupport { -public: - MPRISSupport(); - ~MPRISSupport(); - -private: - class Private; - const std::unique_ptr _private; -}; - -} // namespace internal -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index d5343914f0..edf8a4bc1d 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -29,7 +29,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/linux/base_linux_xdp_utilities.h" #include "platform/linux/linux_notification_service_watcher.h" #include "platform/linux/linux_xdp_file_dialog.h" -#include "platform/linux/linux_mpris_support.h" #include "platform/linux/linux_gsd_media_keys.h" #endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION @@ -544,22 +543,13 @@ void SetDarkMode() { void SetWatchingMediaKeys(bool watching) { #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION - static std::unique_ptr MPRISInstance; static std::unique_ptr GSDInstance; if (watching) { - if (!MPRISInstance) { - MPRISInstance = std::make_unique(); - } - if (!GSDInstance) { GSDInstance = std::make_unique(); } } else { - if (MPRISInstance) { - MPRISInstance = nullptr; - } - if (GSDInstance) { GSDInstance = nullptr; }